Example of Using Asynchronous Method Calls in a Text Search

The example in this section searches for a text string (targetText) in the method source of a set of classes (classList). The example is broken up into a number of work units, to allow the searching of the classes to be carried out by a number of worker applications in parallel. Each work unit is a group of 10 classes from the total list of classes in which the search is performed.

Each work unit uses the searchClassesMethods method in the Application class. The code that demonstrates how the search is implemented is not relevant to the example and is not given.

The signature of the searchClassesMethods method is as follows.

searchClassesMethods(classList:     XClassSet;
                     targetText:    String;
                     caseSensitive: Boolean;
                     matches:       XMethodSet input): Integer;

The classList parameter contains the set of 10 classes for the work unit, and is a collection class with a membership of Class instances. The method searches the local methods of each listed class, adding each method that has the requested text to the matches collection (which is a shared transient instance of a collection type with a membership of Method instances), and returns the count of matches found.

The signature of the method in the originating application that initiates the search is as follows.

parallelSearch10(targetText:       String;
                 caseSensitive:    Boolean;
                 classList:        ClassColl;
                 parallelRequests: Integer);

The target parameter is the search text string and the classList parameter is the list of classes whose methods are to be searched. The caseSensitive parameter specifies whether the search is case-sensitive.

The parallelRequests parameter is the number of parallel work units initiated. In this example, the work units are implemented by instances of a subclass of the JadeMethodContext class called CallContext. The subclass has a non-exclusive matchingMethods reference, which is of type XMethodSet, and a non-exclusive shared transient classList reference, which is of type XClassSet.

The number of parallel work units initiated, which is the number of CallContext objects, differs from the number of worker applications that are running. Work units are queued until worker applications become available.

The code for the parallelSearch10 method is as follows.

vars
    cls:             Class;
    clsIndex:        Integer;
    totalClasses:    Integer;
    totalMatches:    Integer;
    doneClasses:     Integer;
    emptyClasses:    Integer;
    matchCount:      Integer;
    meth:            Method;
    errnum:          Integer;
    countMeths:      Integer;
    inx:             Integer;
    iter:            Iterator;
    running:         Boolean;
    jmthCntxX:       JadeMethodContext;
    callCntxX:       CallContext;
    allCallContexts: CallContextArray;
begin
    totalClasses := classList.size;
    totalMatches := 0;
    emptyClasses := 0;
    // Create a call context for each parallel request
    create allCallContexts transient;
    beginTransientTransaction;
    foreach inx in 1 to parallelRequests do
        create callCntxX transient;
        allCallContexts.add(callCntxX);
        callCntxX.tag := inx;
        callCntxX.workerAppName := "SearchWorker";
        create callCntxX.classList sharedTransient;
        create callCntxX.matchingMethods sharedTransient;
    endforeach;
    commitTransientTransaction;
    // Start the initial requests
    iter := classList.createIterator;
    foreach callCntxX in allCallContexts do
        beginTransientTransaction;
        callCntxX.classList.clear;
        clsIndex := 0;
        while iter.next(cls) do
            countMeths := cls.countLocalMethods;
            if countMeths = 0 then
                doneClasses := doneClasses + 1;
                emptyClasses := emptyClasses + 1;
                continue;
            endif;
            callCntxX.classList.add(cls);
            clsIndex := clsIndex + 1;
            if clsIndex = 10 then
                break;
            endif;
        endwhile;
        commitTransientTransaction;
        if clsIndex > 0 then
            callCntxX.initialize;
            callCntxX.classCount := clsIndex;
            callCntxX.invoke(app, searchClassesMethods, callCntxX.classList,
                      targetText, caseSensitive, callCntxX.matchingMethods);
        endif;
    endforeach;
    running := true;
    while running do
        // wait for a request to complete
        jmthCntxX := process.waitForMethods(allCallContexts);
        if jmthCntxX = null then //No active contexts
            running := false;
        else
            callCntxX := jmthCntxX.CallContext;
            matchCount := 0;
            if callCntxX.isTimedOut then
                errnum := 9997;
            else
                errnum := callCntxX.getErrorNumber;
                if errnum = 0 then
                    matchCount := callCntxX.getReturnValue.Integer;
                endif;
            endif;
            if matchCount > 0 then
                foreach meth in callCntxX.matchingMethods do
                    write "MATCH " & meth.schemaType.schema.name & "::" &
                           meth.schemaType.name & "::" & meth.name;
                endforeach;
                totalMatches := totalMatches + matchCount;
            endif;
            doneClasses := doneClasses + callCntxX.classCount;
            // Start another request
            clsIndex := 0;
            beginTransientTransaction;
            callCntxX.classList.clear;
            while iter.next(cls) do
                countMeths := cls.countLocalMethods;
                if countMeths = 0 then
                    doneClasses := doneClasses + 1;
                    emptyClasses := emptyClasses + 1;
                    continue;
                endif;
                callCntxX.classList.add(cls);
                clsIndex := clsIndex + 1;
                if clsIndex = 10 then
                    break;
                endif;
            endwhile;
            commitTransientTransaction;
            if clsIndex > 0 then
                callCntxX.initialize;
                callCntxX.classCount := clsIndex;
                callCntxX.invoke(app, searchClassesMethods,
                                 callCntxX.classList, targetText,
                                 caseSensitive, callCntxX.matchingMethods);
            endif;
        endif;
    endwhile;
    write "Searched " & doneClasses.String & " of " & totalClasses.String &
          " classes, skipping " & emptyClasses.String & CrLf &
           totalMatches.String & " matches";
epilog
    beginTransientTransaction;
    foreach callCntxX in allCallContexts do
        delete callCntxX.classList;
        delete callCntxX.matchingMethods;
        delete callCntxX;
    endforeach;
    commitTransientTransaction;
    delete allCallContexts;
end;