Activities.Mod 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555
  1. (* Runtime support for activities *)
  2. (* Copyright (C) Florian Negele *)
  3. (** The module provides the runtime support for activities associated with active objects. *)
  4. (** It implements a basic task scheduler that distributes the work of concurrent activities to logical processors. *)
  5. (** In addition, it also provides a framework for implementing synchronisation primitives. *)
  6. MODULE Activities;
  7. IMPORT SYSTEM, BaseTypes, Counters, CPU, Processors, Queues, Timer;
  8. (** Represents one of four different priorities of an activity. *)
  9. TYPE Priority* = SIZE;
  10. CONST SafeStackSize = 512 * SIZE OF ADDRESS;
  11. CONST InitialStackSize = CPU.StackSize;
  12. CONST MaximumStackSize* = 1024 * InitialStackSize;
  13. CONST (** Indicates the lowest priority used for idle processors. *) IdlePriority* = 0;
  14. CONST (** Indicates the default priority of new activities. *) DefaultPriority* = 1;
  15. CONST (** Indicates a higher priority than the default. *) HighPriority* = 2;
  16. CONST (** Indicates the highest of all priorities. *) RealtimePriority* = 3;
  17. CONST LowestPriority = IdlePriority; HighestPriority = RealtimePriority;
  18. CONST Priorities = HighestPriority - LowestPriority + 1;
  19. (** Represents a procedure that is called after the execution of an activity has been suspended by the {{{[[Activities.SwitchTo]]}}} procedure. *)
  20. TYPE SwitchFinalizer* = PROCEDURE (previous {UNTRACED}: Activity; value: ADDRESS);
  21. (* Represents the stack of an activity. *)
  22. TYPE Stack = POINTER {DISPOSABLE} TO ARRAY OF CHAR;
  23. TYPE StackRecord = POINTER {UNSAFE} TO RECORD
  24. prev {UNTRACED}, next {UNTRACED}: Stack;
  25. END;
  26. (** Represents the handler identifying activities that are currently either running or suspended. *)
  27. TYPE Activity* = OBJECT {DISPOSABLE} (Queues.Item)
  28. VAR processor {UNTRACED}: POINTER {UNSAFE} TO Processor;
  29. VAR firstStack {UNTRACED}: Stack;
  30. VAR stackLimit: ADDRESS;
  31. VAR quantum := CPU.Quantum: LONGWORD;
  32. VAR priority: Priority;
  33. VAR finalizer := NIL: SwitchFinalizer;
  34. VAR previous: Activity; argument: ADDRESS;
  35. VAR framePointer: ADDRESS;
  36. VAR procedure: PROCEDURE;
  37. VAR object-: BaseTypes.Object;
  38. VAR bound := FALSE: BOOLEAN;
  39. VAR startTime-: Timer.Counter;
  40. VAR time- := 0: HUGEINT;
  41. VAR stack {UNTRACED}: Stack;
  42. VAR context*: OBJECT;
  43. PROCEDURE &InitializeActivity (procedure: PROCEDURE; priority: Priority);
  44. VAR stackRecord {UNTRACED}: StackRecord; stackFrame {UNTRACED}: BaseTypes.StackFrame;
  45. VAR StackFrameDescriptor {UNTRACED} EXTERN "BaseTypes.StackFrame": BaseTypes.Descriptor;
  46. BEGIN {UNCOOPERATIVE, UNCHECKED}
  47. ASSERT (priority < Priorities);
  48. ASSERT (InitialStackSize > SafeStackSize);
  49. NEW (stack, InitialStackSize);
  50. ASSERT (stack # NIL);
  51. firstStack := stack;
  52. stackRecord := ADDRESS OF stack[0];
  53. stackRecord.next := NIL;
  54. stackRecord.prev := NIL;
  55. stackLimit := ADDRESS OF stack[SafeStackSize+3* SIZE OF ADDRESS]; SELF.priority := priority;
  56. framePointer := ADDRESS OF stack[InitialStackSize - 4 * SIZE OF ADDRESS] - CPU.StackDisplacement;
  57. stackFrame := framePointer + CPU.StackDisplacement;
  58. stackFrame.caller := Start;
  59. stackFrame.previous := NIL;
  60. stackFrame.descriptor := ADDRESS OF StackFrameDescriptor;
  61. SELF.procedure := procedure;
  62. IF SYSTEM.GetActivity () # NIL THEN
  63. context := SYSTEM.GetActivity ()(Activity).context;
  64. ELSE
  65. context := NIL;
  66. END;
  67. END InitializeActivity;
  68. PROCEDURE ~Finalize;
  69. VAR address: ADDRESS; stackFrame {UNTRACED}: BaseTypes.StackFrame; currentActivity {UNTRACED}: Activity; stack{UNTRACED}, next{UNTRACED}: Stack; stackRecord{UNTRACED}: StackRecord;
  70. BEGIN {UNCOOPERATIVE, UNCHECKED}
  71. address := framePointer;
  72. currentActivity := SYSTEM.GetActivity ()(Activity); SYSTEM.SetActivity (SELF);
  73. WHILE address # NIL DO
  74. stackFrame := address + CPU.StackDisplacement;
  75. IF ODD (stackFrame.descriptor) THEN
  76. DEC (stackFrame.descriptor);
  77. stackFrame.Reset;
  78. address := stackFrame.previous;
  79. ELSE
  80. address := stackFrame.descriptor;
  81. END;
  82. END;
  83. SYSTEM.SetActivity (currentActivity);
  84. stack := firstStack;
  85. REPEAT
  86. stackRecord := ADDRESS OF stack[0];
  87. next := stackRecord.next;
  88. DISPOSE (stack);
  89. stack := next;
  90. UNTIL stack = NIL;
  91. Finalize^;
  92. END Finalize;
  93. END Activity;
  94. (* Represents a handler for an activity that is associated with an active object. *)
  95. TYPE Process = OBJECT {DISPOSABLE} (Activity)
  96. PROCEDURE &InitializeProcess (procedure: PROCEDURE; priority: Priority; object: BaseTypes.Object);
  97. BEGIN {UNCOOPERATIVE, UNCHECKED}
  98. InitializeActivity (procedure, priority);
  99. ASSERT (object # NIL);
  100. ASSERT (object.action # NIL);
  101. SELF.object := object;
  102. object.action.activity := SELF;
  103. END InitializeProcess;
  104. PROCEDURE Unlink;
  105. BEGIN {UNCOOPERATIVE, UNCHECKED} object := NIL;
  106. END Unlink;
  107. PROCEDURE ~Finalize;
  108. VAR currentActivity {UNTRACED}: Activity; item: Queues.Item;
  109. BEGIN {UNCOOPERATIVE, UNCHECKED}
  110. IF object # NIL THEN
  111. currentActivity := SYSTEM.GetActivity ()(Activity); SYSTEM.SetActivity (SELF);
  112. object.action.activity := NIL;
  113. IF Queues.Dequeue (item, object.action.waitingQueue) THEN Resume (item(Activity)) END;
  114. Unlink;
  115. SYSTEM.SetActivity (currentActivity);
  116. END;
  117. Finalize^;
  118. END Finalize;
  119. END Process;
  120. (* Stores information per processor. *)
  121. TYPE Processor = RECORD {ALIGNED (CPU.CacheLineSize)}
  122. assigning := FALSE: BOOLEAN;
  123. originalFramePointer: ADDRESS;
  124. readyQueue: ARRAY Priorities OF Queues.AlignedQueue;
  125. runningActivity {UNTRACED}: Activity;
  126. index: SIZE;
  127. END;
  128. VAR processors: ARRAY Processors.Maximum OF Processor;
  129. VAR readyQueue: ARRAY Priorities OF Queues.AlignedQueue;
  130. VAR working, physicalProcessors, virtualProcessors: Counters.AlignedCounter;
  131. (** Stores an atomic counter indicating the number of activities that are awaiting interrupts to occur. *)
  132. (** The scheduler stops its execution if all processors are idle, unless there are activities waiting for interrupts. *)
  133. VAR awaiting*: Counters.AlignedCounter;
  134. PROCEDURE StoreActivity EXTERN "Environment.StoreActivity";
  135. PROCEDURE GetProcessTime-(): HUGEINT;
  136. VAR activity: Activity; diff: Timer.Counter;
  137. BEGIN{UNCOOPERATIVE, UNCHECKED}
  138. activity := SYSTEM.GetActivity ()(Activity);
  139. diff := Timer.GetCounter()-activity.startTime;
  140. RETURN activity.time + diff;
  141. END GetProcessTime;
  142. (** Returns the handler of the current activity executing this procedure call. *)
  143. PROCEDURE GetCurrentActivity- (): {UNTRACED} Activity;
  144. BEGIN {UNCOOPERATIVE, UNCHECKED} RETURN SYSTEM.GetActivity ()(Activity);
  145. END GetCurrentActivity;
  146. (** Returns the unique index of the processor executing this procedure call. *)
  147. PROCEDURE GetCurrentProcessorIndex- (): SIZE;
  148. BEGIN {UNCOOPERATIVE, UNCHECKED} IF SYSTEM.GetActivity () # NIL THEN RETURN SYSTEM.GetActivity ()(Activity).processor.index ELSE RETURN 0 END;
  149. END GetCurrentProcessorIndex;
  150. (** Sets the priority of the current activity calling this procedure and returns the previous value. *)
  151. PROCEDURE SetCurrentPriority- (priority: Priority): Priority;
  152. VAR currentActivity {UNTRACED}: Activity; previousPriority: Priority;
  153. BEGIN {UNCOOPERATIVE, UNCHECKED}
  154. ASSERT (priority < Priorities);
  155. currentActivity := SYSTEM.GetActivity ()(Activity);
  156. previousPriority := currentActivity.priority;
  157. currentActivity.priority := priority;
  158. RETURN previousPriority;
  159. END SetCurrentPriority;
  160. (** Binds the calling activity to the currently executing processor. *)
  161. PROCEDURE BindToCurrentProcessor-;
  162. BEGIN {UNCOOPERATIVE, UNCHECKED}
  163. SYSTEM.GetActivity ()(Activity).bound := TRUE;
  164. END BindToCurrentProcessor;
  165. (** Returns whether there is an activity that is ready to run and has at least the specified priority. *)
  166. PROCEDURE Select- (VAR activity: Activity; minimum: Priority): BOOLEAN;
  167. VAR processor {UNTRACED}: POINTER {UNSAFE} TO Processor;
  168. VAR priority := HighestPriority + 1: Priority; item: Queues.Item;
  169. BEGIN {UNCOOPERATIVE, UNCHECKED}
  170. processor := SYSTEM.GetActivity ()(Activity).processor;
  171. REPEAT
  172. DEC (priority);
  173. IF Queues.Dequeue (item, processor.readyQueue[priority]) THEN
  174. activity := item(Activity); RETURN TRUE;
  175. ELSIF Queues.Dequeue (item, readyQueue[priority]) THEN
  176. activity := item(Activity);
  177. IF activity.bound & (activity.processor # processor) THEN
  178. Enqueue (activity, ADDRESS OF activity.processor.readyQueue[activity.priority]);
  179. ELSE
  180. RETURN TRUE;
  181. END;
  182. END;
  183. UNTIL priority = minimum;
  184. RETURN FALSE;
  185. END Select;
  186. (** Performs a cooperative task switch by suspending the execution of the current activity and resuming the execution of any other activity that is ready to continue. *)
  187. (** This procedure is called by the compiler whenever it detects that the time quantum of the current activity has expired. *)
  188. PROCEDURE Switch-;
  189. VAR currentActivity {UNTRACED}, nextActivity: Activity;
  190. BEGIN {UNCOOPERATIVE, UNCHECKED}
  191. currentActivity := SYSTEM.GetActivity ()(Activity);
  192. IF Select (nextActivity, currentActivity.priority) THEN
  193. SwitchTo (nextActivity, Enqueue, ADDRESS OF readyQueue[currentActivity.priority]);
  194. FinalizeSwitch;
  195. ELSE
  196. currentActivity.quantum := CPU.Quantum;
  197. END;
  198. END Switch;
  199. (* Switch finalizer that enqueues the previous activity to the specified ready queue. *)
  200. PROCEDURE Enqueue (previous {UNTRACED}: Activity; queue {UNTRACED}: POINTER {UNSAFE} TO Queues.Queue);
  201. BEGIN {UNCOOPERATIVE, UNCHECKED}
  202. Queues.Enqueue (previous, queue^);
  203. IF ADDRESS OF queue^ = ADDRESS OF readyQueue[IdlePriority] THEN RETURN END;
  204. IF Counters.Read (working) < Processors.count THEN Processors.ResumeAllProcessors END;
  205. END Enqueue;
  206. (** Resumes the execution of an activity that was suspended by a call to the {{{[[Activities.SwitchTo]]}}} procedure beforehand. *)
  207. PROCEDURE Resume- (activity: Activity);
  208. BEGIN {UNCOOPERATIVE, UNCHECKED}
  209. ASSERT (activity # NIL);
  210. Enqueue (activity, ADDRESS OF readyQueue[activity.priority])
  211. END Resume;
  212. (** Performs a synchronous task switch. *)
  213. (** The resumed activity continues its execution by first calling the specified finalizer procedure with the given argument. *)
  214. (** Each invocation of this procedure must be directly followed by a call to the {{{[[Activities.FinalizeSwitch]]}}} procedure. *)
  215. PROCEDURE SwitchTo- (VAR activity: Activity; finalizer: SwitchFinalizer; argument: ADDRESS);
  216. VAR currentActivity {UNTRACED}, nextActivity {UNTRACED}: Activity; diff: Timer.Counter;
  217. BEGIN {UNCOOPERATIVE, UNCHECKED}
  218. IF activity.bound & (activity.processor # SYSTEM.GetActivity ()(Activity).processor) THEN
  219. REPEAT UNTIL Select (nextActivity, IdlePriority);
  220. Resume (activity); activity := nextActivity;
  221. END;
  222. currentActivity := SYSTEM.GetActivity ()(Activity);
  223. currentActivity.framePointer := SYSTEM.GetFramePointer ();
  224. currentActivity.quantum := CPU.Quantum;
  225. diff := Timer.GetCounter() - currentActivity.startTime;
  226. currentActivity.time := currentActivity.time + diff;
  227. nextActivity := activity;
  228. nextActivity.processor := currentActivity.processor;
  229. nextActivity.finalizer := finalizer;
  230. nextActivity.argument := argument;
  231. nextActivity.previous := currentActivity;
  232. nextActivity.processor.runningActivity := nextActivity;
  233. nextActivity.startTime := Timer.GetCounter();
  234. activity := NIL;
  235. SYSTEM.SetActivity (nextActivity); StoreActivity;
  236. SYSTEM.SetFramePointer (nextActivity.framePointer);
  237. END SwitchTo;
  238. (** Finalizes a task switch performed by calling the switch finalizer of the previously suspended activity. *)
  239. (** This procedure must be called after each invocation of the {{{[[Activities.SwitchTo]]}}} procedure. *)
  240. PROCEDURE FinalizeSwitch-;
  241. VAR currentActivity {UNTRACED}: Activity;
  242. BEGIN {UNCOOPERATIVE, UNCHECKED}
  243. currentActivity := SYSTEM.GetActivity ()(Activity);
  244. IF currentActivity.finalizer # NIL THEN currentActivity.finalizer (currentActivity.previous, currentActivity.argument) END;
  245. currentActivity.finalizer := NIL; currentActivity.previous := NIL;
  246. END FinalizeSwitch;
  247. (* Entry point for new activities. *)
  248. PROCEDURE Start;
  249. VAR currentActivity {UNTRACED}: Activity;
  250. VAR procedure {UNTRACED}: POINTER {UNSAFE} TO RECORD body: PROCEDURE (object {UNTRACED}: OBJECT) END;
  251. BEGIN {UNCOOPERATIVE, UNCHECKED}
  252. FinalizeSwitch;
  253. currentActivity := SYSTEM.GetActivity ()(Activity);
  254. procedure := ADDRESS OF currentActivity.procedure;
  255. procedure.body (currentActivity.object);
  256. TerminateCurrentActivity;
  257. END Start;
  258. (** This procedure is called by the compiler for each {{{NEW}}} statement that creates an active object. *)
  259. (** It associates an active object with a new activity that begins its execution with the specified body procedure. *)
  260. PROCEDURE Create- (body: PROCEDURE; priority: Priority; object {UNTRACED}: BaseTypes.Object);
  261. VAR activity: Process;
  262. BEGIN {UNCOOPERATIVE, UNCHECKED}
  263. IF priority = IdlePriority THEN priority := SYSTEM.GetActivity ()(Activity).priority END;
  264. NEW (activity, body, priority, object);
  265. ASSERT (activity # NIL);
  266. Resume (activity);
  267. END Create;
  268. (** Creates an activity that pretends to be executed on a distinct processor. *)
  269. PROCEDURE CreateVirtualProcessor- (): {UNTRACED} Activity;
  270. VAR activity {UNTRACED}: Activity; index: SIZE;
  271. BEGIN {UNCOOPERATIVE, UNCHECKED}
  272. NEW (activity, NIL, DefaultPriority);
  273. ASSERT (activity # NIL);
  274. index := Counters.Increment (virtualProcessors, 1) + Processors.count;
  275. ASSERT (index < Processors.Maximum);
  276. activity.processor := ADDRESS OF processors[index];
  277. activity.processor.index := index;
  278. activity.bound := TRUE;
  279. RETURN activity;
  280. END CreateVirtualProcessor;
  281. (** Temporarily exchanges the currently running activity with a virtual processor in order to call the specified procedure in a different context. *)
  282. PROCEDURE CallVirtual- (procedure: PROCEDURE (value: ADDRESS); value: ADDRESS; processor {UNTRACED}: Activity);
  283. VAR currentActivity {UNTRACED}: Activity; stackPointer: ADDRESS;
  284. BEGIN {UNCOOPERATIVE, UNCHECKED}
  285. ASSERT (processor # NIL);
  286. currentActivity := SYSTEM.GetActivity ()(Activity); stackPointer := SYSTEM.GetStackPointer (); SYSTEM.SetActivity (processor); StoreActivity;
  287. SYSTEM.SetStackPointer (ADDRESS OF processor.stack[LEN (processor.stack) - CPU.StackDisplacement]);
  288. procedure (value); SYSTEM.SetActivity (currentActivity); StoreActivity; SYSTEM.SetStackPointer (stackPointer);
  289. END CallVirtual;
  290. (** Creates a new activity that calls the specified procedure. *)
  291. PROCEDURE Call- (procedure: PROCEDURE);
  292. VAR activity: Activity;
  293. BEGIN {UNCOOPERATIVE, UNCHECKED}
  294. NEW (activity, procedure, DefaultPriority);
  295. ASSERT (activity # NIL);
  296. Resume (activity);
  297. END Call;
  298. (** Starts the scheduler on the current processor by creating a new activity that calls the specified procedure. *)
  299. (** This procedure is called by the runtime system once during the initialization of each processor. *)
  300. PROCEDURE Execute- (procedure: PROCEDURE);
  301. VAR previousActivity {UNTRACED}: Activity;
  302. BEGIN {UNCOOPERATIVE, UNCHECKED}
  303. SYSTEM.SetActivity (NIL);
  304. BeginExecution (procedure);
  305. previousActivity := SYSTEM.GetActivity ()(Activity);
  306. previousActivity.processor.runningActivity := NIL;
  307. SYSTEM.SetActivity (NIL);
  308. Dispose (previousActivity, NIL);
  309. END Execute;
  310. (* Turns the calling procedure temporarily into an activity that begins its execution with the specified procedure. *)
  311. PROCEDURE BeginExecution (procedure: PROCEDURE);
  312. VAR activity {UNTRACED}: Activity; index: SIZE;
  313. BEGIN {UNCOOPERATIVE, UNCHECKED}
  314. NEW (activity, procedure, DefaultPriority);
  315. ASSERT (activity # NIL);
  316. index := Counters.Increment (physicalProcessors, 1);
  317. ASSERT (index < Processors.count);
  318. activity.processor := ADDRESS OF processors[index];
  319. activity.processor.originalFramePointer := SYSTEM.GetFramePointer ();
  320. activity.processor.runningActivity := activity; activity.processor.index := index;
  321. ASSERT (Counters.Increment (working, 1) < Processors.count);
  322. IF (index = 0) & (Processors.count > 1) THEN Processors.StartAll END;
  323. SYSTEM.SetActivity (activity); SYSTEM.SetFramePointer (activity.framePointer);
  324. END BeginExecution;
  325. (* Yields the execution of the current activity to any activity with the given minimal priority. *)
  326. PROCEDURE YieldExecution (minimum: Priority; finalizer: SwitchFinalizer; value: ADDRESS);
  327. VAR nextActivity: Activity;
  328. BEGIN {UNCOOPERATIVE, UNCHECKED}
  329. LOOP
  330. IF Select (nextActivity, minimum) THEN
  331. SwitchTo (nextActivity, finalizer, value);
  332. FinalizeSwitch;
  333. ELSE
  334. IF Counters.Decrement (working, 1) + Counters.Read (awaiting) > 1 THEN Processors.SuspendCurrentProcessor END;
  335. IF Counters.Increment (working, 1) + Counters.Read (awaiting) = 0 THEN EXIT END;
  336. END;
  337. END;
  338. END YieldExecution;
  339. (* This procedure returns to the procedure that called BeginExecution. *)
  340. PROCEDURE EndExecution;
  341. VAR currentActivity {UNTRACED}: Activity;
  342. BEGIN {UNCOOPERATIVE, UNCHECKED}
  343. currentActivity := SYSTEM.GetActivity ()(Activity);
  344. currentActivity.framePointer := SYSTEM.GetFramePointer ();
  345. IF Counters.Decrement (working, 1) < Processors.count THEN Processors.ResumeAllProcessors END;
  346. SYSTEM.SetFramePointer (currentActivity.processor.originalFramePointer);
  347. END EndExecution;
  348. (** This is the default procedure for initially idle processors starting the scheduler using the {{{[[Activities.Execute]]}}} procedure. *)
  349. PROCEDURE Idle-;
  350. BEGIN {UNCOOPERATIVE, UNCHECKED}
  351. ASSERT (SetCurrentPriority (IdlePriority) = DefaultPriority);
  352. YieldExecution (IdlePriority + 1, Enqueue, ADDRESS OF readyQueue[IdlePriority]); EndExecution;
  353. END Idle;
  354. (** Terminates the execution of the current activity calling this procedure. *)
  355. (** This procedure is also invoked at the end of the body of an active object. *)
  356. PROCEDURE {NORETURN} TerminateCurrentActivity-;
  357. BEGIN {UNCOOPERATIVE, UNCHECKED}
  358. YieldExecution (IdlePriority, Dispose, NIL);
  359. EndExecution; HALT (1234);
  360. END TerminateCurrentActivity;
  361. (* Switch finalizer that disposes the resources of the terminated activity. *)
  362. PROCEDURE Dispose (previous {UNTRACED}: Activity; value: ADDRESS);
  363. BEGIN {UNCOOPERATIVE, UNCHECKED}
  364. DISPOSE (previous);
  365. END Dispose;
  366. (** This procedure is called by the compiler while executing a {{{WAIT}}} statement. *)
  367. (** It awaits the termination of all activities associated with an active object. *)
  368. PROCEDURE Wait- (object {UNTRACED}: BaseTypes.Object);
  369. VAR nextActivity: Activity; item: Queues.Item;
  370. BEGIN {UNCOOPERATIVE, UNCHECKED}
  371. ASSERT (object # NIL);
  372. IF object.action = NIL THEN RETURN END;
  373. IF object.action.activity = NIL THEN RETURN END;
  374. REPEAT UNTIL Select (nextActivity, IdlePriority);
  375. SwitchTo (nextActivity, EnqueueWaiting, object.action); FinalizeSwitch;
  376. WHILE Queues.Dequeue (item, object.action.waitingQueue) DO Resume (item (Activity)) END;
  377. END Wait;
  378. (* Switch finalizer that enqueues acitivities waiting on an active object. *)
  379. PROCEDURE EnqueueWaiting (previous {UNTRACED}: Activity; action {UNTRACED}: POINTER {UNSAFE} TO BaseTypes.Action);
  380. VAR item: Queues.Item;
  381. BEGIN {UNCOOPERATIVE, UNCHECKED}
  382. Queues.Enqueue (previous, action.waitingQueue);
  383. IF action.activity # NIL THEN RETURN END;
  384. IF Queues.Dequeue (item, action.waitingQueue) THEN Resume (item (Activity)) END;
  385. END EnqueueWaiting;
  386. PROCEDURE ReturnToStackSegment*;
  387. VAR
  388. stackFrame {UNTRACED}: BaseTypes.StackFrame;
  389. currentActivity {UNTRACED}: Activity;
  390. newStack {UNTRACED}: Stack;
  391. stackRecord {UNTRACED}: StackRecord;
  392. BEGIN{UNCOOPERATIVE, UNCHECKED}
  393. (* old stack pointer and base pointer have been pushed again, we have to revert this *)
  394. stackFrame := SYSTEM.GetFramePointer();
  395. (*
  396. TRACE(stackFrame.caller);
  397. TRACE(stackFrame.previous);
  398. previousFrame := stackFrame.previous;
  399. TRACE(ADDRESS OF previousFrame.caller + SIZE OF ADDRESS);
  400. TRACE(previousFrame.caller);
  401. TRACE(previousFrame.previous);
  402. *)
  403. currentActivity := SYSTEM.GetActivity ()(Activity);
  404. stackRecord := ADDRESS OF currentActivity.stack[0];
  405. newStack := stackRecord.prev;
  406. currentActivity.stack := newStack;
  407. currentActivity.stackLimit := ADDRESS OF newStack[SafeStackSize + 3 * SIZE OF ADDRESS];
  408. END ReturnToStackSegment;
  409. PROCEDURE {NOPAF} ReturnToStackSegment0;
  410. BEGIN{UNCOOPERATIVE, UNCHECKED}
  411. CPU.SaveResult;
  412. ReturnToStackSegment;
  413. CPU.RestoreResultAndReturn;
  414. END ReturnToStackSegment0;
  415. (** Expands the stack memory of the current activity to include the specified stack address and returns the new stack pointer to be set after the call. *)
  416. PROCEDURE ExpandStack- (address: ADDRESS; parSize: SIZE): ADDRESS;
  417. VAR
  418. currentActivity {UNTRACED}: Activity;
  419. varSize, minSize, newSize: SIZE; sp: ADDRESS;
  420. newStack {UNTRACED}: POINTER {DISPOSABLE} TO ARRAY OF CHAR;
  421. stackFrame {UNTRACED}, previousFrame {UNTRACED}, newFrame {UNTRACED}: BaseTypes.StackFrame;
  422. stackRecord{UNTRACED}, newStackRecord{UNTRACED}: StackRecord;
  423. BEGIN {UNCOOPERATIVE, UNCHECKED}
  424. (* check for valid argument *)
  425. currentActivity := SYSTEM.GetActivity ()(Activity);
  426. stackFrame := SYSTEM.GetFramePointer ();
  427. previousFrame := stackFrame.previous;
  428. varSize := stackFrame.previous - address;
  429. (*
  430. TRACE(SYSTEM.GetFramePointer(), address, varSize, parSize, size, stackFrame.caller);
  431. *)
  432. ASSERT(varSize >= 0);
  433. ASSERT(parSize >= 0);
  434. newSize := LEN (currentActivity.stack); (* current stack size *)
  435. minSize := SafeStackSize + parSize + varSize + 3 * SIZEOF(ADDRESS) (* stack frame *) + 3 * SIZEOF(ADDRESS) (* prev, next *);
  436. REPEAT INC (newSize, newSize) UNTIL newSize >= minSize;
  437. ASSERT (newSize <= MaximumStackSize);
  438. stackRecord := ADDRESS OF currentActivity.stack[0];
  439. newStack := stackRecord.next;
  440. IF (newStack = NIL) OR (LEN(newStack) < newSize) THEN
  441. NEW (newStack, newSize);
  442. ASSERT (newStack # NIL);
  443. newStackRecord := ADDRESS OF newStack[0];
  444. newStackRecord.prev := currentActivity.stack;
  445. newStackRecord.next := NIL;
  446. stackRecord.next := newStack;
  447. ELSE
  448. newStackRecord := ADDRESS OF newStack[0];
  449. ASSERT(newStackRecord.prev = currentActivity.stack);
  450. ASSERT(stackRecord.next = newStack);
  451. END;
  452. newSize := LEN(newStack);
  453. newFrame := ADDRESS OF newStack[0] + newSize- parSize - 3*SIZE OF ADDRESS;
  454. newFrame.previous := stackFrame.previous;
  455. newFrame.descriptor := previousFrame.descriptor;
  456. newFrame.caller := ReturnToStackSegment0;
  457. previousFrame.descriptor := stackFrame.descriptor; (* trick to get a base stack frame descriptor *)
  458. stackFrame.previous := newFrame;
  459. SYSTEM.MOVE(ADDRESS OF previousFrame.caller + SIZE OF ADDRESS, ADDRESS OF newFrame.caller + SIZE OF ADDRESS, parSize); (* copy parameters *)
  460. sp := ADDRESSOF(newFrame.descriptor) - varSize;
  461. DISPOSE (currentActivity.stack); currentActivity.stack := newStack;
  462. currentActivity.stackLimit := ADDRESS OF newStack[SafeStackSize + 3 * SIZE OF ADDRESS];
  463. RETURN sp;
  464. END ExpandStack;
  465. (** Returns whether the specified address corresponds to a local variable that resides on the stack of the current activity calling this procedure. *)
  466. PROCEDURE IsLocalVariable- (address: ADDRESS): BOOLEAN;
  467. VAR currentActivity {UNTRACED}: Activity; begin, end: ADDRESS; stack {UNTRACED}: Stack; stackRecord {UNTRACED}: StackRecord;
  468. BEGIN {UNCOOPERATIVE, UNCHECKED}
  469. currentActivity := SYSTEM.GetActivity ()(Activity);
  470. IF currentActivity = NIL THEN RETURN FALSE END;
  471. stack := currentActivity.firstStack;
  472. REPEAT
  473. begin := ADDRESS OF stack[0];
  474. end := begin + LEN (stack);
  475. IF (address >= begin) & (address < end) THEN RETURN TRUE END;
  476. stackRecord := begin;
  477. stack := stackRecord.next;
  478. UNTIL stack = NIL;
  479. RETURN FALSE;
  480. END IsLocalVariable;
  481. (** Returns whether any activity is currently executing an assignment statement. *)
  482. PROCEDURE AssignmentsInProgress- (): BOOLEAN;
  483. VAR i: SIZE;
  484. BEGIN {UNCOOPERATIVE, UNCHECKED}
  485. FOR i := 0 TO Processors.Maximum - 1 DO IF processors[i].assigning THEN RETURN TRUE END END; RETURN FALSE;
  486. END AssignmentsInProgress;
  487. (** Terminates the module and disposes all of its resources. *)
  488. PROCEDURE Terminate-;
  489. VAR priority: Priority;
  490. BEGIN {UNCOOPERATIVE, UNCHECKED}
  491. FOR priority := LowestPriority TO HighestPriority DO
  492. Queues.Dispose (readyQueue[priority]);
  493. END;
  494. END Terminate;
  495. END Activities.