|
|
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
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.