Annotation of Examples/AppKit/SortingInAction/GenericSort.m, revision 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.