Annotation of Examples/AppKit/SortingInAction/GenericSort.m, revision 1.1.1.1

1.1       root        1: 
                      2: /* The GenericSort class is the focal class of this program.  It encapsulates 
                      3:  * all the functionality for a sort object:  keeping track of tick counts, 
                      4:  * knowing how to animate the sort, controlling the speed, managing the 
                      5:  * data.  GenericSort only lacks a specific sorting algorithm.  This makes 
                      6:  * way for a great inheritance--each of the specific sort classes needs to 
                      7:  * override -init (to assign their unique sort name) and to create a -sort 
                      8:  * method.  That's it.  In the sort method, the subclasses use the superclass 
                      9:  * methods -lessThan:: and -swap:with: and so on to compare and exchange 
                     10:  * values, and the superclass deals with all the implementation details of 
                     11:  * animating, speed control, pacing the sorts, etc.  Adding a new sort 
                     12:  * algorithm is then trivial, you only need to code the algorithm!
                     13:  * Because all the sorting occurs in a separate thread, Generic Sort will cons 
                     14:  * up a message to send to the main thread when it is time to do any drawing.
                     15:  *
                     16:  * Author: Julie Zelenski, NeXT Developer Support
                     17:  * You may freely copy, distribute and reuse the code in this example.  
                     18:  * NeXT disclaims any warranty of any kind, expressed or implied, as to 
                     19:  * its fitness for any particular use.
                     20:  */
                     21: 
                     22: 
                     23: #import "GenericSort.h"
                     24: #import "SortView.h"
                     25: #import "SortApp.h"
                     26: #import <mach/cthreads.h>
                     27: 
                     28: extern BOOL Abort;             // global variable to signal abort 
                     29: extern int TickCount;                  // global variable of current tick count 
                     30: extern mutex_t TickLock;       // mutual exclusion to protect TickCount 
                     31: extern condition_t TickUpdated;        // signaled when TickCount is increased 
                     32: 
                     33: 
                     34: @implementation GenericSort:Object
                     35: 
                     36: 
                     37: /* Static class variables */
                     38: static int SortsRunning;       // number of sorts fighting for processor
                     39: static mutex_t SortsLock;      // mutual exclusion to protect SortsRunning 
                     40: 
                     41: 
                     42: /* FACTORY METHODS */
                     43: 
                     44: + initialize
                     45: /* +initialize is sent to the factory object for GenericSort before any 
                     46:  * other messages are sent.  This is a good place to allocate and initialize 
                     47:  * static class variables.
                     48:  */
                     49: {
                     50:     SortsRunning = 0;
                     51:     SortsLock = mutex_alloc();
                     52:     return self;
                     53: }
                     54: 
                     55: 
                     56: - init
                     57: /* The initialization method sets up a new sort.  The sort creates its own 
                     58:  * sortView, and initalizes its instance variables to their default values.  
                     59:  * It also builds the message struct it will send as a drawing message.
                     60:  */
                     61: {
                     62:     [super init];
                     63:     sortView = [[SortView allocFromZone:[self zone]] initSort:self]; 
                     64:     compareVal = 1;
                     65:     moveVal = 2;
                     66:     fcallVal = 8;
                     67:     animate = NO;
                     68:     speedDelay = 0;
                     69:     drawMsg.h.msg_simple = TRUE;       // no ports or out-of-line data
                     70:     drawMsg.h.msg_size = sizeof(simpleMsg);
                     71:     drawMsg.h.msg_local_port = PORT_NULL;
                     72:     drawMsg.h.msg_type = MSG_TYPE_NORMAL;
                     73:     drawMsg.t.msg_type_name = MSG_TYPE_INTEGER_32;
                     74:     drawMsg.t.msg_type_size = 32;
                     75:     drawMsg.t.msg_type_number = MAXDATA;
                     76:     drawMsg.t.msg_type_inline = TRUE;  // no out-of-line data will be passed
                     77:     drawMsg.t.msg_type_longform = FALSE;
                     78:     drawMsg.t.msg_type_deallocate = FALSE;
                     79:     return self;
                     80: }
                     81: 
                     82: 
                     83: 
                     84: /* TARGET-ACTION METHODS */
                     85: 
                     86: - setTicks:(int)value for:(int)tag;
                     87: /* The SortController passes along this method to each sort when the user 
                     88:  * changes the ticks weighting of an operation.  This method assigns
                     89:  * the new tick value.
                     90:  */
                     91: {
                     92: #define COMPARE 3
                     93: #define MOVE 4
                     94: #define FCALL 5        
                     95: 
                     96:     switch (tag) {
                     97:         case COMPARE:  compareVal = value; 
                     98:                        break;
                     99:         case MOVE:     moveVal = value;
                    100:                        break;
                    101:         case FCALL:    fcallVal = value;
                    102:                        break;
                    103:     }
                    104:     return self;
                    105: }
                    106: 
                    107: 
                    108: - setAnimate:(BOOL)value;
                    109: /* The SortController passes along this method to each sort when the user 
                    110:  * changes the animate compares switch.  This method can be sent while the
                    111:  * sort is sorting, and it immediately takes effect--the next comparison 
                    112:  * will highlight or not, depending on the new value.
                    113:  */
                    114: {
                    115:     animate = value;
                    116:     return self;
                    117: }
                    118: 
                    119: 
                    120: - setSpeed:(int)value;
                    121: /* The SortController passes along this method to each sort when the user 
                    122:  * moves the speed slider.  This method can be sent while the sort is sorting, 
                    123:  * and it immediately takes effect--the sorts will begin to slow down or 
                    124:  * speed up accordingly. 
                    125:  */
                    126: {
                    127:     speedDelay = value;
                    128:     return self;
                    129: }
                    130: 
                    131: 
                    132: - setSize:(int)size address:(int*)address
                    133: /* Called by the SortController when the user has started a sort.  This method
                    134:  * makes its own private copy of the data set generated by the SortController 
                    135:  * and grabs a hold of the port to send drawing messages to.  Then it sends 
                    136:  * a drawing message to the main thread to draw each of the bars.
                    137:  */
                    138: {
                    139:     int c;
                    140: 
                    141:     numElements = size;
                    142:     [sortView setUpForSize:numElements]; //calculate size and placement of bars
                    143:     data = (int *)calloc(numElements,sizeof(int));
                    144:     drawMsg.h.msg_remote_port = [NXApp drawPort]; //set port to send drawing to
                    145:     drawMsg.sortNum = sortNum; // first parameter in drawMsg is my sort number
                    146:     for (c = 0; c < numElements; c++) {        // for each element in the data set
                    147:         data[c] = address[c];
                    148:         drawMsg.h.msg_id = MOVE_MSG;
                    149:         drawMsg.position[0] = c;
                    150:         drawMsg.value[0] = data[c];
                    151:         drawMsg.value[1] = 0;
                    152:         msg_send((msg_header_t *)&drawMsg,MSG_OPTION_NONE,0); 
                    153:     }
                    154:     return self;
                    155: }
                    156: 
                    157: 
                    158: 
                    159: /* PRIVATE METHODS */
                    160: 
                    161: - abort;
                    162: /* Called when the sort notices that the global variable Abort has been 
                    163:  * signaled. This calls sortDone to clean up after itself and exits the 
                    164:  * cthread.
                    165:  */
                    166: {
                    167:     [self sortDone];           // signal done
                    168:     cthread_exit(0);           // kill thread
                    169:     return nil;
                    170: }
                    171: 
                    172: 
                    173: - sortDone;
                    174: /* Called when a sort has exited, either because it completed sorting or Abort 
                    175:  * was signaled.  It sends one last message to the drawPort to draw the 
                    176:  * finished sortView.  It also grabs the mutex for SortsRunning and decrements 
                    177:  * it by one.  It signals TickUpdated so any other thread waiting will wake up.
                    178:  */ 
                    179: {
                    180:     drawMsg.h.msg_id = FINISHED_MSG;    // send finished message to main thread
                    181:     msg_send((msg_header_t *)&drawMsg,MSG_OPTION_NONE,0); // draw finished
                    182:     mutex_lock(SortsLock);             // must protect shared global!!
                    183:     SortsRunning--;                    // decrement number of sorts running
                    184:     mutex_unlock(SortsLock);
                    185:     condition_signal(TickUpdated);     // wake up any sleeping threads
                    186:     if (data) cfree(data);             // free the data set
                    187:     numElements = 0;
                    188:     return self;
                    189: }
                    190: 
                    191: 
                    192: 
                    193: static void myWait(int millisecs)
                    194: /* Because the unix function usleep() is not thread-safe, I wrote a little 
                    195:  * function to delay the sorts for a specified number of milliseconds. 
                    196:  * Basically it allocates a port for itself and then tries to receive a 
                    197:  * message on that port.  I don't send any messages to that port, so it is 
                    198:  * waiting futilely.  However, I indicate the msg_receive should time out in 
                    199:  * the number of milliseconds I wanted to wait.
                    200:  */
                    201: {
                    202:     msg_header_t nullMsg;
                    203:     port_t aPort;
                    204:     
                    205:     if (port_allocate(task_self(), &aPort) != KERN_SUCCESS) return;
                    206:     nullMsg.msg_local_port = aPort;
                    207:     nullMsg.msg_size = sizeof(nullMsg);
                    208:     
                    209:     msg_receive(&nullMsg,RCV_TIMEOUT,millisecs);// wait until timeout
                    210:     port_deallocate(task_self(),aPort);                // clean up
                    211: }
                    212: 
                    213: 
                    214: - adjust      
                    215: /* Adjust is called to make the sorts out in front wait for the other sorts 
                    216:  * to catch up before going on.  If this thread is in the lead (i.e. if its 
                    217:  * tick count is ahead of the global variable TickCount), it will increment 
                    218:  * TickCount, signal the condition TickUpdated (to wake up other sleeping 
                    219:  * threads) and then put itself to sleep waiting for another sort to catch up 
                    220:  * and signal the TickUpdated condition.  Of course, if this is the only sort 
                    221:  * running (if SortsRunning = 1) then I don't put myself into endless sleep 
                    222:  * waiting for a non-existent sort to catch up with me!  
                    223:  * The adjust method also is used to regulate the speed (corresponding to the 
                    224:  * speed slider).  Each thread will delay a number of milliseconds in this 
                    225:  * method based on how slow the animation should be.  (see myWait() function 
                    226:  * above)  At top speed, the sorts delay 0 milliseconds, but in slow motion 
                    227:  * they can delay up to half a second in this method.
                    228:  */
                    229: {   
                    230:     int ticks;
                    231:     
                    232:     if (speedDelay) myWait(speedDelay);        // wait speedDelay number of mseconds
                    233:     ticks = compareVal*compares + moveVal*moves + fcallVal*fcalls;
                    234:     mutex_lock(TickLock);
                    235:     if (ticks > TickCount) {           // if out in front
                    236:        TickCount = ticks;              // increment counter
                    237:        if (SortsRunning > 1) { // if other sorts are running
                    238:            condition_signal(TickUpdated); // signal counter has incremented
                    239:            condition_wait(TickUpdated,TickLock); // wait til counter updated
                    240:         }
                    241:     }
                    242:     mutex_unlock(TickLock); 
                    243:     if (Abort) [self abort];
                    244:     return self;
                    245: }
                    246: 
                    247: 
                    248: - swap:(int)position1 with:(int)position2
                    249: /* To exchange values, the specific sort subclasses use the generic sort method 
                    250:  * swap:with.  This method exchanges the data elements, and sends a message 
                    251:  * to the main thread to do the drawing for that swap.  Before messaging, it 
                    252:  * calls the adjust method to make sure that sorts out in front wait for the 
                    253:  * other sorts to catch up before going on.  NOTE: Swap expects that the 
                    254:  * smaller value is in position1. 
                    255:  */
                    256: {    
                    257:     int temp;
                    258:     
                    259:     moves += 2;     // a swap involves two moves
                    260:     [self adjust];  
                    261:     drawMsg.h.msg_id = SWAP_MSG;       // send a swap message to main thread
                    262:     drawMsg.position[0] = position1;
                    263:     drawMsg.value[0] = data[position1];
                    264:     drawMsg.position[1] = position2;
                    265:     drawMsg.value[1] = data[position2];
                    266:     msg_send((msg_header_t *)&drawMsg,MSG_OPTION_NONE,0);
                    267:     temp = data[position1];
                    268:     data[position1] = data[position2];
                    269:     data[position2] = temp;
                    270:     return self;
                    271: }
                    272: 
                    273: 
                    274: - moveValue:(int)value to:(int)position
                    275: /* To change the value at a specific position, the specific sort subclasses 
                    276:  * use the generic sort method moveValue:to.  This method assigns the new value 
                    277:  * to the element, the data elements, and sends a message to the main thread to 
                    278:  * do the drawing for that move.  Before messaging, it calls the adjust method 
                    279:  * to make sure that sorts out in front wait for the other sorts to catch up 
                    280:  * before going on.  
                    281:  */
                    282: {      
                    283:     moves++;
                    284:     [self adjust]; 
                    285:     drawMsg.h.msg_id = MOVE_MSG;       // send a move message to main thread
                    286:     drawMsg.value[0] = value;
                    287:     drawMsg.position[0] = position;
                    288:     drawMsg.value[1] = data[position];
                    289:     msg_send((msg_header_t *)&drawMsg,MSG_OPTION_NONE,0);
                    290:     data[position] = value;  
                    291:     return self;
                    292: }
                    293: 
                    294: 
                    295: - (BOOL)lessThan:(int)position1 :(int)position2
                    296: /* To compares two values in the data set, the specific sort subclasses 
                    297:  * use the generic sort method lessThan::.  This method returns the value 
                    298:  * of the comparison, and if the sort is currently animating compares, it 
                    299:  * will send a message to the main thread highlight the two elements being 
                    300:  * compared.  Before messaging, it calls the adjust method to make sure that 
                    301:  * sorts out in front wait for the other sorts to catch up before going on.  
                    302:  */
                    303: {        
                    304:     compares++;     
                    305:     [self adjust];
                    306:     if (animate) {                     // if animating compares
                    307:        drawMsg.h.msg_id = COMPARE_MSG; // send compare message to main thread
                    308:        drawMsg.position[0] = position1;
                    309:        drawMsg.value[0] = data[position1];
                    310:        drawMsg.position[1] = position2;
                    311:        drawMsg.value[1] = data[position2];
                    312:        msg_send((msg_header_t *)&drawMsg,MSG_OPTION_NONE,0);    
                    313:     }
                    314:     return (data[position1] < data[position2]);
                    315: }
                    316: 
                    317: 
                    318: /* PUBLIC METHODS */
                    319: 
                    320: - sort;  
                    321: /* The method is the heart of the GenericSort class.  It is called to do the 
                    322:  * actual sorting.  The specific subclasses of GenericSort should override this 
                    323:  * method, making sure that the first line of their -sort method is call 
                    324:  * [super sort].  In the super version, this method is mostly a stub but it 
                    325:  * does perform a few important tasks, notably setting the counters to zero and 
                    326:  * adding this sort to the number of sorts currently running.
                    327:  */ 
                    328: {
                    329:     compares = moves = fcalls = 0;
                    330:     mutex_lock(SortsLock);     // must protect share global!!
                    331:     SortsRunning++;
                    332:     mutex_unlock(SortsLock);
                    333:     drawMsg.h.msg_id = SORTING_MSG;            // send message to main thread
                    334:     msg_send((msg_header_t *)&drawMsg,MSG_OPTION_NONE,0);  // status "Sorting"
                    335:     return self;
                    336: }
                    337: 
                    338: 
                    339: 
                    340: /* These methods simply retrieve instance variables of the sort object */
                    341: 
                    342: - view;
                    343: {   
                    344:     return sortView; 
                    345: }
                    346: - (const char*)getName;
                    347: {
                    348:     return sortName;
                    349: }
                    350: - (int)compares
                    351: {
                    352:     return compares;
                    353: }
                    354: - (int)moves
                    355: {
                    356:     return moves;
                    357: }
                    358: - (int)fcalls;
                    359: {
                    360:     return fcalls;
                    361: }
                    362: - (int)totalTicks
                    363: {
                    364:     return (compareVal*compares + moveVal*moves + fcallVal*fcalls);
                    365: }
                    366: 
                    367: @end

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.