OpenCores
URL https://opencores.org/ocsvn/scarts/scarts/trunk

Subversion Repositories scarts

[/] [scarts/] [trunk/] [toolchain/] [scarts-gcc/] [gcc-4.1.1/] [gcc/] [ada/] [s-taprop-os2.adb] - Blame information for rev 20

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 12 jlechner
------------------------------------------------------------------------------
2
--                                                                          --
3
--                  GNAT RUN-TIME LIBRARY (GNARL) COMPONENTS                --
4
--                                                                          --
5
--    S Y S T E M . T A S K _ P R I M I T I V E S . O P E R A T I O N S     --
6
--                                                                          --
7
--                                  B o d y                                 --
8
--                                                                          --
9
--         Copyright (C) 1992-2005, Free Software Foundation, Inc.          --
10
--                                                                          --
11
-- GNARL is free software; you can  redistribute it  and/or modify it under --
12
-- terms of the  GNU General Public License as published  by the Free Soft- --
13
-- ware  Foundation;  either version 2,  or (at your option) any later ver- --
14
-- sion. GNARL is distributed in the hope that it will be useful, but WITH- --
15
-- OUT ANY WARRANTY;  without even the  implied warranty of MERCHANTABILITY --
16
-- or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License --
17
-- for  more details.  You should have  received  a copy of the GNU General --
18
-- Public License  distributed with GNARL; see file COPYING.  If not, write --
19
-- to  the  Free Software Foundation,  51  Franklin  Street,  Fifth  Floor, --
20
-- Boston, MA 02110-1301, USA.                                              --
21
--                                                                          --
22
-- As a special exception,  if other files  instantiate  generics from this --
23
-- unit, or you link  this unit with other files  to produce an executable, --
24
-- this  unit  does not  by itself cause  the resulting  executable  to  be --
25
-- covered  by the  GNU  General  Public  License.  This exception does not --
26
-- however invalidate  any other reasons why  the executable file  might be --
27
-- covered by the  GNU Public License.                                      --
28
--                                                                          --
29
-- GNARL was developed by the GNARL team at Florida State University.       --
30
-- Extensive contributions were provided by Ada Core Technologies, Inc.     --
31
--                                                                          --
32
------------------------------------------------------------------------------
33
 
34
--  This is an OS/2 version of this package
35
 
36
--  This package contains all the GNULL primitives that interface directly
37
--  with the underlying OS.
38
 
39
pragma Polling (Off);
40
--  Turn off polling, we do not want ATC polling to take place during
41
--  tasking operations. It causes infinite loops and other problems.
42
 
43
with System.Tasking.Debug;
44
--  used for Known_Tasks
45
 
46
with System.OS_Primitives;
47
--  used for Delay_Modes
48
--           Clock
49
 
50
with Interfaces.OS2Lib.Errors;
51
with Interfaces.OS2Lib.Threads;
52
with Interfaces.OS2Lib.Synchronization;
53
 
54
with Interfaces.C;
55
--  used for size_t
56
 
57
with Interfaces.C.Strings;
58
--  used for Null_Ptr
59
 
60
with System.Parameters;
61
--  used for Size_Type
62
 
63
with Unchecked_Conversion;
64
with Unchecked_Deallocation;
65
 
66
package body System.Task_Primitives.Operations is
67
 
68
   package IC  renames Interfaces.C;
69
   package ICS renames Interfaces.C.Strings;
70
   package OSP renames System.OS_Primitives;
71
 
72
   use Interfaces.OS2Lib;
73
   use Interfaces.OS2Lib.Errors;
74
   use Interfaces.OS2Lib.Threads;
75
   use Interfaces.OS2Lib.Synchronization;
76
   use System.Parameters;
77
   use System.Tasking.Debug;
78
   use System.Tasking;
79
   use System.OS_Interface;
80
   use Interfaces.C;
81
   use System.OS_Primitives;
82
 
83
   ---------------------
84
   -- Local Constants --
85
   ---------------------
86
 
87
   Max_Locks_Per_Task   : constant := 100;
88
   Suppress_Owner_Check : constant Boolean := False;
89
 
90
   -----------------
91
   -- Local Types --
92
   -----------------
93
 
94
   subtype Lock_Range is Integer range 0 .. Max_Locks_Per_Task;
95
 
96
   -----------------
97
   -- Local Data  --
98
   -----------------
99
 
100
   --  The OS/2 DosAllocThreadLocalMemory API is used to allocate our TCB_Ptr
101
 
102
   --  This API reserves a small range of virtual addresses that is backed
103
   --  by different physical memory for each running thread. In this case we
104
   --  create a pointer at a fixed address that points to the TCB_Ptr for the
105
   --  running thread. So all threads will be able to query and update their
106
   --  own TCB_Ptr without destroying the TCB_Ptr of other threads.
107
 
108
   type Thread_Local_Data is record
109
      Self_ID           : Task_Id;    --  ID of the current thread
110
      Lock_Prio_Level   : Lock_Range; --  Nr of priority changes due to locks
111
 
112
      --  ... room for expansion here, if we decide to make access to
113
      --  jump-buffer and exception stack more efficient in future
114
   end record;
115
 
116
   type Access_Thread_Local_Data is access all Thread_Local_Data;
117
 
118
   --  Pointer to Thread Local Data
119
   Thread_Local_Data_Ptr : aliased Access_Thread_Local_Data;
120
 
121
   type PPTLD is access all Access_Thread_Local_Data;
122
 
123
   Single_RTS_Lock : aliased RTS_Lock;
124
   --  This is a lock to allow only one thread of control in the RTS at
125
   --  a time; it is used to execute in mutual exclusion from all other tasks.
126
   --  Used mainly in Single_Lock mode, but also to protect All_Tasks_List
127
 
128
   Environment_Task_Id : Task_Id;
129
   --  A variable to hold Task_Id for the environment task
130
 
131
   -----------------------
132
   -- Local Subprograms --
133
   -----------------------
134
 
135
   function To_PPVOID is new Unchecked_Conversion (PPTLD, PPVOID);
136
   function To_Address is new Unchecked_Conversion (Task_Id, System.Address);
137
   function To_PFNTHREAD is
138
     new Unchecked_Conversion (System.Address, PFNTHREAD);
139
 
140
   function To_MS (D : Duration) return ULONG;
141
 
142
   procedure Set_Temporary_Priority
143
     (T            : in Task_Id;
144
      New_Priority : in System.Any_Priority);
145
 
146
   -----------
147
   -- To_MS --
148
   -----------
149
 
150
   function To_MS (D : Duration) return ULONG is
151
   begin
152
      return ULONG (D * 1_000);
153
   end To_MS;
154
 
155
   -----------
156
   -- Clock --
157
   -----------
158
 
159
   function Monotonic_Clock return Duration renames OSP.Monotonic_Clock;
160
 
161
   -------------------
162
   -- RT_Resolution --
163
   -------------------
164
 
165
   function RT_Resolution return Duration is
166
   begin
167
      return 10#1.0#E-6;
168
   end RT_Resolution;
169
 
170
   -------------------
171
   -- Abort_Handler --
172
   -------------------
173
 
174
   --  OS/2 only has limited support for asynchronous signals.
175
   --  It seems not to be possible to jump out of an exception
176
   --  handler or to change the execution context of the thread.
177
   --  So asynchonous transfer of control is not supported.
178
 
179
   -----------------
180
   -- Stack_Guard --
181
   -----------------
182
 
183
   --  The underlying thread system sets a guard page at the
184
   --  bottom of a thread stack, so nothing is needed.
185
   --  ??? Check the comment above
186
 
187
   procedure Stack_Guard (T : ST.Task_Id; On : Boolean) is
188
      pragma Unreferenced (T);
189
      pragma Unreferenced (On);
190
   begin
191
      null;
192
   end Stack_Guard;
193
 
194
   --------------------
195
   -- Get_Thread_Id  --
196
   --------------------
197
 
198
   function Get_Thread_Id (T : ST.Task_Id) return OSI.Thread_Id is
199
   begin
200
      return OSI.Thread_Id (T.Common.LL.Thread);
201
   end Get_Thread_Id;
202
 
203
   ----------
204
   -- Self --
205
   ----------
206
 
207
   function Self return Task_Id is
208
      Self_ID : Task_Id renames Thread_Local_Data_Ptr.Self_ID;
209
 
210
   begin
211
      --  Check that the thread local data has been initialized
212
 
213
      pragma Assert
214
        ((Thread_Local_Data_Ptr /= null
215
          and then Thread_Local_Data_Ptr.Self_ID /= null));
216
 
217
      return Self_ID;
218
   end Self;
219
 
220
   ---------------------
221
   -- Initialize_Lock --
222
   ---------------------
223
 
224
   procedure Initialize_Lock
225
     (Prio : System.Any_Priority;
226
      L    : access Lock)
227
   is
228
   begin
229
      if DosCreateMutexSem
230
        (ICS.Null_Ptr, L.Mutex'Unchecked_Access, 0, False32) /= NO_ERROR
231
      then
232
         raise Storage_Error;
233
      end if;
234
 
235
      pragma Assert (L.Mutex /= 0, "Error creating Mutex");
236
      L.Priority := Prio;
237
      L.Owner_ID := Null_Address;
238
   end Initialize_Lock;
239
 
240
   procedure Initialize_Lock (L : access RTS_Lock; Level : Lock_Level) is
241
      pragma Unreferenced (Level);
242
 
243
   begin
244
      if DosCreateMutexSem
245
        (ICS.Null_Ptr, L.Mutex'Unchecked_Access, 0, False32) /= NO_ERROR
246
      then
247
         raise Storage_Error;
248
      end if;
249
 
250
      pragma Assert (L.Mutex /= 0, "Error creating Mutex");
251
 
252
      L.Priority := System.Any_Priority'Last;
253
      L.Owner_ID := Null_Address;
254
   end Initialize_Lock;
255
 
256
   -------------------
257
   -- Finalize_Lock --
258
   -------------------
259
 
260
   procedure Finalize_Lock (L : access Lock) is
261
   begin
262
      Must_Not_Fail (DosCloseMutexSem (L.Mutex));
263
   end Finalize_Lock;
264
 
265
   procedure Finalize_Lock (L : access RTS_Lock) is
266
   begin
267
      Must_Not_Fail (DosCloseMutexSem (L.Mutex));
268
   end Finalize_Lock;
269
 
270
   ----------------
271
   -- Write_Lock --
272
   ----------------
273
 
274
   procedure Write_Lock (L : access Lock; Ceiling_Violation : out Boolean) is
275
      Self_ID      : constant Task_Id := Thread_Local_Data_Ptr.Self_ID;
276
      Old_Priority : constant Any_Priority :=
277
                       Self_ID.Common.LL.Current_Priority;
278
 
279
   begin
280
      if L.Priority < Old_Priority then
281
         Ceiling_Violation := True;
282
         return;
283
      end if;
284
 
285
      Ceiling_Violation := False;
286
 
287
      --  Increase priority before getting the lock
288
      --  to prevent priority inversion
289
 
290
      Thread_Local_Data_Ptr.Lock_Prio_Level :=
291
        Thread_Local_Data_Ptr.Lock_Prio_Level + 1;
292
      if L.Priority > Old_Priority then
293
         Set_Temporary_Priority (Self_ID, L.Priority);
294
      end if;
295
 
296
      --  Request the lock and then update the lock owner data
297
 
298
      Must_Not_Fail (DosRequestMutexSem (L.Mutex, SEM_INDEFINITE_WAIT));
299
      L.Owner_Priority := Old_Priority;
300
      L.Owner_ID := Self_ID.all'Address;
301
   end Write_Lock;
302
 
303
   procedure Write_Lock
304
     (L           : access RTS_Lock;
305
      Global_Lock : Boolean := False)
306
   is
307
      Self_ID      : Task_Id;
308
      Old_Priority : Any_Priority;
309
 
310
   begin
311
      if not Single_Lock or else Global_Lock then
312
         Self_ID := Thread_Local_Data_Ptr.Self_ID;
313
         Old_Priority := Self_ID.Common.LL.Current_Priority;
314
 
315
         --  Increase priority before getting the lock
316
         --  to prevent priority inversion
317
 
318
         Thread_Local_Data_Ptr.Lock_Prio_Level :=
319
           Thread_Local_Data_Ptr.Lock_Prio_Level + 1;
320
 
321
         if L.Priority > Old_Priority then
322
            Set_Temporary_Priority (Self_ID, L.Priority);
323
         end if;
324
 
325
         --  Request the lock and then update the lock owner data
326
 
327
         Must_Not_Fail (DosRequestMutexSem (L.Mutex, SEM_INDEFINITE_WAIT));
328
         L.Owner_Priority := Old_Priority;
329
         L.Owner_ID := Self_ID.all'Address;
330
      end if;
331
   end Write_Lock;
332
 
333
   procedure Write_Lock (T : Task_Id) is
334
   begin
335
      if not Single_Lock then
336
 
337
         --  Request the lock and then update the lock owner data
338
 
339
         Must_Not_Fail
340
           (DosRequestMutexSem (T.Common.LL.L.Mutex, SEM_INDEFINITE_WAIT));
341
         T.Common.LL.L.Owner_ID := Null_Address;
342
      end if;
343
   end Write_Lock;
344
 
345
   ---------------
346
   -- Read_Lock --
347
   ---------------
348
 
349
   procedure Read_Lock
350
     (L : access Lock; Ceiling_Violation : out Boolean) renames Write_Lock;
351
 
352
   ------------
353
   -- Unlock --
354
   ------------
355
 
356
   procedure Unlock (L : access Lock) is
357
      Self_ID      : constant Task_Id := Thread_Local_Data_Ptr.Self_ID;
358
      Old_Priority : constant Any_Priority := L.Owner_Priority;
359
 
360
   begin
361
      --  Check that this task holds the lock
362
 
363
      pragma Assert (Suppress_Owner_Check
364
        or else L.Owner_ID = Self_ID.all'Address);
365
 
366
      --  Upate the owner data
367
 
368
      L.Owner_ID := Null_Address;
369
 
370
      --  Do the actual unlocking. No more references
371
      --  to owner data of L after this point.
372
 
373
      Must_Not_Fail (DosReleaseMutexSem (L.Mutex));
374
 
375
      --  Reset priority after unlocking to avoid priority inversion
376
 
377
      Thread_Local_Data_Ptr.Lock_Prio_Level :=
378
        Thread_Local_Data_Ptr.Lock_Prio_Level - 1;
379
      if L.Priority /= Old_Priority then
380
         Set_Temporary_Priority (Self_ID, Old_Priority);
381
      end if;
382
   end Unlock;
383
 
384
   procedure Unlock (L : access RTS_Lock; Global_Lock : Boolean := False) is
385
      Self_ID      : Task_Id;
386
      Old_Priority : Any_Priority;
387
 
388
   begin
389
      if not Single_Lock or else Global_Lock then
390
         Self_ID := Thread_Local_Data_Ptr.Self_ID;
391
         Old_Priority := L.Owner_Priority;
392
         --  Check that this task holds the lock
393
 
394
         pragma Assert (Suppress_Owner_Check
395
           or else L.Owner_ID = Self_ID.all'Address);
396
 
397
         --  Upate the owner data
398
 
399
         L.Owner_ID := Null_Address;
400
 
401
         --  Do the actual unlocking. No more references
402
         --  to owner data of L after this point.
403
 
404
         Must_Not_Fail (DosReleaseMutexSem (L.Mutex));
405
 
406
         --  Reset priority after unlocking to avoid priority inversion
407
 
408
         Thread_Local_Data_Ptr.Lock_Prio_Level :=
409
           Thread_Local_Data_Ptr.Lock_Prio_Level - 1;
410
 
411
         if L.Priority /= Old_Priority then
412
            Set_Temporary_Priority (Self_ID, Old_Priority);
413
         end if;
414
      end if;
415
   end Unlock;
416
 
417
   procedure Unlock (T : Task_Id) is
418
   begin
419
      if not Single_Lock then
420
 
421
         --  Check the owner data
422
 
423
         pragma Assert (Suppress_Owner_Check
424
           or else T.Common.LL.L.Owner_ID = Null_Address);
425
 
426
         --  Do the actual unlocking. No more references
427
         --  to owner data of T.Common.LL.L after this point.
428
 
429
         Must_Not_Fail (DosReleaseMutexSem (T.Common.LL.L.Mutex));
430
      end if;
431
   end Unlock;
432
 
433
   -----------
434
   -- Sleep --
435
   -----------
436
 
437
   procedure Sleep
438
     (Self_ID : Task_Id;
439
      Reason  : System.Tasking.Task_States)
440
   is
441
      pragma Unreferenced (Reason);
442
 
443
      Count : aliased ULONG; -- Used to store dummy result
444
 
445
   begin
446
      --  Must reset Cond BEFORE L is unlocked
447
 
448
      Sem_Must_Not_Fail
449
        (DosResetEventSem (Self_ID.Common.LL.CV, Count'Unchecked_Access));
450
 
451
      if Single_Lock then
452
         Unlock_RTS;
453
      else
454
         Unlock (Self_ID);
455
      end if;
456
 
457
      --  No problem if we are interrupted here.
458
      --  If the condition is signaled, DosWaitEventSem will simply not block.
459
 
460
      Sem_Must_Not_Fail
461
        (DosWaitEventSem (Self_ID.Common.LL.CV, SEM_INDEFINITE_WAIT));
462
 
463
      --  Since L was previously accquired, lock operation should not fail
464
 
465
      if Single_Lock then
466
         Lock_RTS;
467
      else
468
         Write_Lock (Self_ID);
469
      end if;
470
   end Sleep;
471
 
472
   -----------------
473
   -- Timed_Sleep --
474
   -----------------
475
 
476
   --  This is for use within the run-time system, so abort is
477
   --  assumed to be already deferred, and the caller should be
478
   --  holding its own ATCB lock.
479
 
480
   --  Pre-assertion: Cond is posted
481
   --                 Self is locked.
482
 
483
   --  Post-assertion: Cond is posted
484
   --                  Self is locked.
485
 
486
   procedure Timed_Sleep
487
     (Self_ID  : Task_Id;
488
      Time     : Duration;
489
      Mode     : ST.Delay_Modes;
490
      Reason   : System.Tasking.Task_States;
491
      Timedout : out Boolean;
492
      Yielded  : out Boolean)
493
   is
494
      pragma Unreferenced (Reason);
495
 
496
      Check_Time : constant Duration := OSP.Monotonic_Clock;
497
      Rel_Time   : Duration;
498
      Abs_Time   : Duration;
499
      Time_Out   : ULONG;
500
      Result    : APIRET;
501
      Count      : aliased ULONG;  --  Used to store dummy result
502
 
503
   begin
504
      --  Must reset Cond BEFORE Self_ID is unlocked
505
 
506
      Sem_Must_Not_Fail
507
        (DosResetEventSem (Self_ID.Common.LL.CV,
508
         Count'Unchecked_Access));
509
 
510
      if Single_Lock then
511
         Unlock_RTS;
512
      else
513
         Unlock (Self_ID);
514
      end if;
515
 
516
      Timedout := True;
517
      Yielded := False;
518
 
519
      if Mode = Relative then
520
         Rel_Time := Time;
521
         Abs_Time := Duration'Min (Time, Max_Sensible_Delay) + Check_Time;
522
      else
523
         Rel_Time := Time - Check_Time;
524
         Abs_Time := Time;
525
      end if;
526
 
527
      if Rel_Time > 0.0 then
528
         loop
529
            exit when Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level
530
              or else Self_ID.Pending_Priority_Change;
531
 
532
            Time_Out := To_MS (Rel_Time);
533
            Result := DosWaitEventSem (Self_ID.Common.LL.CV, Time_Out);
534
            pragma Assert
535
             ((Result = NO_ERROR or Result = ERROR_TIMEOUT
536
                or Result = ERROR_INTERRUPT));
537
 
538
            --  ???
539
            --  What to do with error condition ERROR_NOT_ENOUGH_MEMORY? Can
540
            --  we raise an exception here?  And what about ERROR_INTERRUPT?
541
            --  Should that be treated as a simple timeout?
542
            --  For now, consider only ERROR_TIMEOUT to be a timeout.
543
 
544
            exit when Abs_Time <= OSP.Monotonic_Clock;
545
 
546
            if Result /= ERROR_TIMEOUT then
547
               --  somebody may have called Wakeup for us
548
               Timedout := False;
549
               exit;
550
            end if;
551
 
552
            Rel_Time := Abs_Time - OSP.Monotonic_Clock;
553
         end loop;
554
      end if;
555
 
556
      --  Ensure post-condition
557
 
558
      if Single_Lock then
559
         Lock_RTS;
560
      else
561
         Write_Lock (Self_ID);
562
      end if;
563
 
564
      if Timedout then
565
         Sem_Must_Not_Fail (DosPostEventSem (Self_ID.Common.LL.CV));
566
      end if;
567
   end Timed_Sleep;
568
 
569
   -----------------
570
   -- Timed_Delay --
571
   -----------------
572
 
573
   procedure Timed_Delay
574
     (Self_ID  : Task_Id;
575
      Time     : Duration;
576
      Mode     : ST.Delay_Modes)
577
   is
578
      Check_Time : constant Duration := OSP.Monotonic_Clock;
579
      Rel_Time   : Duration;
580
      Abs_Time   : Duration;
581
      Timedout   : Boolean := True;
582
      Time_Out   : ULONG;
583
      Result     : APIRET;
584
      Count      : aliased ULONG;  --  Used to store dummy result
585
 
586
   begin
587
      if Single_Lock then
588
         Lock_RTS;
589
      else
590
         Write_Lock (Self_ID);
591
      end if;
592
 
593
      --  Must reset Cond BEFORE Self_ID is unlocked
594
 
595
      Sem_Must_Not_Fail
596
        (DosResetEventSem (Self_ID.Common.LL.CV,
597
         Count'Unchecked_Access));
598
 
599
      if Single_Lock then
600
         Unlock_RTS;
601
      else
602
         Unlock (Self_ID);
603
      end if;
604
 
605
      if Mode = Relative then
606
         Rel_Time := Time;
607
         Abs_Time := Time + Check_Time;
608
      else
609
         Rel_Time := Time - Check_Time;
610
         Abs_Time := Time;
611
      end if;
612
 
613
      if Rel_Time > 0.0 then
614
         Self_ID.Common.State := Delay_Sleep;
615
 
616
         loop
617
            if Self_ID.Pending_Priority_Change then
618
               Self_ID.Pending_Priority_Change := False;
619
               Self_ID.Common.Base_Priority := Self_ID.New_Base_Priority;
620
               Set_Priority (Self_ID, Self_ID.Common.Base_Priority);
621
            end if;
622
 
623
            exit when Self_ID.Pending_ATC_Level < Self_ID.ATC_Nesting_Level;
624
 
625
            Time_Out := To_MS (Rel_Time);
626
            Result := DosWaitEventSem (Self_ID.Common.LL.CV, Time_Out);
627
 
628
            exit when Abs_Time <= OSP.Monotonic_Clock;
629
 
630
            Rel_Time := Abs_Time - OSP.Monotonic_Clock;
631
         end loop;
632
 
633
         Self_ID.Common.State := Runnable;
634
         Timedout := Result = ERROR_TIMEOUT;
635
      end if;
636
 
637
      if Single_Lock then
638
         Lock_RTS;
639
      else
640
         Write_Lock (Self_ID);
641
      end if;
642
 
643
      if Timedout then
644
         Sem_Must_Not_Fail (DosPostEventSem (Self_ID.Common.LL.CV));
645
      end if;
646
 
647
      if Single_Lock then
648
         Unlock_RTS;
649
      else
650
         Unlock (Self_ID);
651
      end if;
652
 
653
      System.OS_Interface.Yield;
654
   end Timed_Delay;
655
 
656
   ------------
657
   -- Wakeup --
658
   ------------
659
 
660
   procedure Wakeup (T : Task_Id; Reason : System.Tasking.Task_States) is
661
      pragma Unreferenced (Reason);
662
   begin
663
      Sem_Must_Not_Fail (DosPostEventSem (T.Common.LL.CV));
664
   end Wakeup;
665
 
666
   -----------
667
   -- Yield --
668
   -----------
669
 
670
   procedure Yield (Do_Yield : Boolean := True) is
671
   begin
672
      if Do_Yield then
673
         System.OS_Interface.Yield;
674
      end if;
675
   end Yield;
676
 
677
   ----------------------------
678
   -- Set_Temporary_Priority --
679
   ----------------------------
680
 
681
   procedure Set_Temporary_Priority
682
     (T            : Task_Id;
683
      New_Priority : System.Any_Priority)
684
   is
685
      use Interfaces.C;
686
      Delta_Priority : Integer;
687
 
688
   begin
689
      --  When Lock_Prio_Level = 0, we always need to set the
690
      --  Active_Priority. In this way we can make priority changes
691
      --  due to locking independent of those caused by calling
692
      --  Set_Priority.
693
 
694
      if Thread_Local_Data_Ptr.Lock_Prio_Level = 0
695
        or else New_Priority < T.Common.Current_Priority
696
      then
697
         Delta_Priority := T.Common.Current_Priority -
698
           T.Common.LL.Current_Priority;
699
      else
700
         Delta_Priority := New_Priority - T.Common.LL.Current_Priority;
701
      end if;
702
 
703
      if Delta_Priority /= 0 then
704
         --  ??? There is a race-condition here
705
         --  The TCB is updated before the system call to make
706
         --  pre-emption in the critical section less likely.
707
 
708
         T.Common.LL.Current_Priority :=
709
           T.Common.LL.Current_Priority + Delta_Priority;
710
         Must_Not_Fail
711
           (DosSetPriority (Scope   => PRTYS_THREAD,
712
                            Class   => PRTYC_NOCHANGE,
713
                            Delta_P => IC.long (Delta_Priority),
714
                            PorTid  => T.Common.LL.Thread));
715
      end if;
716
   end Set_Temporary_Priority;
717
 
718
   ------------------
719
   -- Set_Priority --
720
   ------------------
721
 
722
   procedure Set_Priority
723
     (T                   : Task_Id;
724
      Prio                : System.Any_Priority;
725
      Loss_Of_Inheritance : Boolean := False)
726
   is
727
      pragma Unreferenced (Loss_Of_Inheritance);
728
   begin
729
      T.Common.Current_Priority := Prio;
730
      Set_Temporary_Priority (T, Prio);
731
   end Set_Priority;
732
 
733
   ------------------
734
   -- Get_Priority --
735
   ------------------
736
 
737
   function Get_Priority (T : Task_Id) return System.Any_Priority is
738
   begin
739
      return T.Common.Current_Priority;
740
   end Get_Priority;
741
 
742
   ----------------
743
   -- Enter_Task --
744
   ----------------
745
 
746
   procedure Enter_Task (Self_ID : Task_Id) is
747
   begin
748
      --  Initialize thread local data. Must be done first
749
 
750
      Thread_Local_Data_Ptr.Self_ID := Self_ID;
751
      Thread_Local_Data_Ptr.Lock_Prio_Level := 0;
752
 
753
      Lock_RTS;
754
 
755
      for J in Known_Tasks'Range loop
756
         if Known_Tasks (J) = null then
757
            Known_Tasks (J) := Self_ID;
758
            Self_ID.Known_Tasks_Index := J;
759
            exit;
760
         end if;
761
      end loop;
762
 
763
      Unlock_RTS;
764
 
765
      --  For OS/2, we can set Self_ID.Common.LL.Thread in
766
      --  Create_Task, since the thread is created suspended.
767
      --  That is, there is no danger of the thread racing ahead
768
      --  and trying to reference Self_ID.Common.LL.Thread before it
769
      --  has been initialized.
770
 
771
      --  .... Do we need to do anything with signals for OS/2 ???
772
   end Enter_Task;
773
 
774
   --------------
775
   -- New_ATCB --
776
   --------------
777
 
778
   function New_ATCB (Entry_Num : Task_Entry_Index) return Task_Id is
779
   begin
780
      return new Ada_Task_Control_Block (Entry_Num);
781
   end New_ATCB;
782
 
783
   -------------------
784
   -- Is_Valid_Task --
785
   -------------------
786
 
787
   function Is_Valid_Task return Boolean is
788
   begin
789
      return False;
790
   end Is_Valid_Task;
791
 
792
   -----------------------------
793
   -- Register_Foreign_Thread --
794
   -----------------------------
795
 
796
   function Register_Foreign_Thread return Task_Id is
797
   begin
798
      return null;
799
   end Register_Foreign_Thread;
800
 
801
   --------------------
802
   -- Initialize_TCB --
803
   --------------------
804
 
805
   procedure Initialize_TCB (Self_ID : Task_Id; Succeeded : out Boolean) is
806
   begin
807
      if DosCreateEventSem (ICS.Null_Ptr,
808
        Self_ID.Common.LL.CV'Unchecked_Access, 0, True32) = NO_ERROR
809
      then
810
         if not Single_Lock
811
           and then DosCreateMutexSem
812
             (ICS.Null_Ptr,
813
              Self_ID.Common.LL.L.Mutex'Unchecked_Access,
814
              0,
815
              False32) /= NO_ERROR
816
         then
817
            Succeeded := False;
818
            Must_Not_Fail (DosCloseEventSem (Self_ID.Common.LL.CV));
819
         else
820
            Succeeded := True;
821
         end if;
822
 
823
         --  We now want to do the equivalent of:
824
 
825
         --  Initialize_Lock
826
         --    (Self_ID.Common.LL.L'Unchecked_Access, ATCB_Level);
827
 
828
         --  But we avoid that because the Initialize_TCB routine has an
829
         --  exception handler, and it is too early for us to deal with
830
         --  installing handlers (see comment below), so we do our own
831
         --  Initialize_Lock operation manually.
832
 
833
         Self_ID.Common.LL.L.Priority := System.Any_Priority'Last;
834
         Self_ID.Common.LL.L.Owner_ID := Null_Address;
835
 
836
      else
837
         Succeeded := False;
838
      end if;
839
 
840
      --  Note: at one time we had an exception handler here, whose code
841
      --  was as follows:
842
 
843
      --  exception
844
 
845
      --     Assumes any failure must be due to insufficient resources
846
 
847
      --     when Storage_Error =>
848
      --        Must_Not_Fail (DosCloseEventSem (Self_ID.Common.LL.CV));
849
      --        Succeeded := False;
850
 
851
      --  but that won't work with the old exception scheme, since it would
852
      --  result in messing with Jmpbuf values too early. If and when we get
853
      --  switched entirely to the new zero-cost exception scheme, we could
854
      --  put this handler back in!
855
   end Initialize_TCB;
856
 
857
   -----------------
858
   -- Create_Task --
859
   -----------------
860
 
861
   procedure Create_Task
862
     (T          : Task_Id;
863
      Wrapper    : System.Address;
864
      Stack_Size : System.Parameters.Size_Type;
865
      Priority   : System.Any_Priority;
866
      Succeeded  : out Boolean)
867
   is
868
      Result              : aliased APIRET;
869
      Adjusted_Stack_Size : System.Parameters.Size_Type;
870
      use System.Parameters;
871
 
872
   begin
873
      --  In OS/2 the allocated stack size should be based on the
874
      --  amount of address space that should be reserved for the stack.
875
      --  Actual memory will only be used when the stack is touched anyway.
876
 
877
      --  The new minimum size is 12 kB, although the EMX docs
878
      --  recommend a minimum size of 32 kB.  (The original was 4 kB)
879
      --  Systems that use many tasks (say > 30) and require much
880
      --  memory may run out of virtual address space, since OS/2
881
      --  has a per-proces limit of 512 MB, of which max. 300 MB is
882
      --  usable in practise.
883
 
884
      if Stack_Size = Unspecified_Size then
885
         Adjusted_Stack_Size := Default_Stack_Size;
886
 
887
      elsif Stack_Size < Minimum_Stack_Size then
888
         Adjusted_Stack_Size := Minimum_Stack_Size;
889
 
890
      else
891
         Adjusted_Stack_Size := Stack_Size;
892
      end if;
893
 
894
      --  GB970222:
895
      --    Because DosCreateThread is called directly here, the
896
      --    C RTL doesn't get initialized for the new thead. EMX by
897
      --    default uses per-thread local heaps in addition to the
898
      --    global heap. There might be other effects of by-passing the
899
      --    C library here.
900
 
901
      --    When using _beginthread the newly created thread is not
902
      --    blocked initially. Does this matter or can I create the
903
      --    thread running anyway? The LL.Thread variable will be set
904
      --    anyway because the variable is passed by reference to OS/2.
905
 
906
      T.Common.LL.Wrapper := To_PFNTHREAD (Wrapper);
907
 
908
      --  The OS implicitly gives the new task the priority of this task
909
 
910
      T.Common.LL.Current_Priority := Self.Common.LL.Current_Priority;
911
 
912
      --  If task was locked before activator task was
913
      --  initialized, assume it has OS standard priority
914
 
915
      if T.Common.LL.L.Owner_Priority not in Any_Priority'Range then
916
         T.Common.LL.L.Owner_Priority := 1;
917
      end if;
918
 
919
      --  Create the thread, in blocked mode
920
 
921
      Result := DosCreateThread
922
        (F_ptid   => T.Common.LL.Thread'Unchecked_Access,
923
         pfn      => T.Common.LL.Wrapper,
924
         param    => To_Address (T),
925
         flag     => Block_Child + Commit_Stack,
926
         cbStack  => ULONG (Adjusted_Stack_Size));
927
 
928
      Succeeded := (Result = NO_ERROR);
929
 
930
      if not Succeeded then
931
         return;
932
      end if;
933
 
934
      --  Set the new thread's priority
935
      --  (child has inherited priority from parent)
936
 
937
      Set_Priority (T, Priority);
938
 
939
      --  Start the thread executing
940
 
941
      Must_Not_Fail (DosResumeThread (T.Common.LL.Thread));
942
 
943
   end Create_Task;
944
 
945
   ------------------
946
   -- Finalize_TCB --
947
   ------------------
948
 
949
   procedure Finalize_TCB (T : Task_Id) is
950
      Tmp    : Task_Id := T;
951
 
952
      procedure Free is new
953
        Unchecked_Deallocation (Ada_Task_Control_Block, Task_Id);
954
 
955
   begin
956
      Must_Not_Fail (DosCloseEventSem (T.Common.LL.CV));
957
 
958
      if not Single_Lock then
959
         Finalize_Lock (T.Common.LL.L'Unchecked_Access);
960
      end if;
961
 
962
      if T.Known_Tasks_Index /= -1 then
963
         Known_Tasks (T.Known_Tasks_Index) := null;
964
      end if;
965
 
966
      Free (Tmp);
967
   end Finalize_TCB;
968
 
969
   ---------------
970
   -- Exit_Task --
971
   ---------------
972
 
973
   procedure Exit_Task is
974
   begin
975
      Thread_Local_Data_Ptr := null;
976
   end Exit_Task;
977
 
978
   ----------------
979
   -- Abort_Task --
980
   ----------------
981
 
982
   procedure Abort_Task (T : Task_Id) is
983
      pragma Unreferenced (T);
984
 
985
   begin
986
      null;
987
 
988
      --  Task abort not implemented yet.
989
      --  Should perform other action ???
990
 
991
   end Abort_Task;
992
 
993
   ----------------
994
   -- Initialize --
995
   ----------------
996
 
997
   procedure Initialize (S : in out Suspension_Object) is
998
      Result : Interfaces.C.int;
999
   begin
1000
      --  Initialize internal state. It is always initialized to False (ARM
1001
      --  D.10 par. 6).
1002
 
1003
      S.State := False;
1004
      S.Waiting := False;
1005
 
1006
      --  Initialize internal mutex
1007
      if DosCreateMutexSem
1008
        (ICS.Null_Ptr, S.L'Unchecked_Access, 0, False32) /= NO_ERROR
1009
      then
1010
         raise Storage_Error;
1011
      end if;
1012
 
1013
      pragma Assert (S.L /= 0, "Error creating Mutex");
1014
 
1015
      --  Initialize internal condition variable
1016
 
1017
      if DosCreateEventSem
1018
        (ICS.Null_Ptr, S.CV'Unchecked_Access, 0, True32) /= NO_ERROR
1019
      then
1020
         Must_Not_Fail (DosCloseMutexSem (S.L));
1021
 
1022
         raise Storage_Error;
1023
      end if;
1024
 
1025
      pragma Assert (S.CV /= 0, "Error creating Condition Variable");
1026
   end Initialize;
1027
 
1028
   --------------
1029
   -- Finalize --
1030
   --------------
1031
 
1032
   procedure Finalize (S : in out Suspension_Object) is
1033
   begin
1034
      --  Destroy internal mutex
1035
 
1036
      Must_Not_Fail (DosCloseMutexSem (S.L'Access));
1037
 
1038
      --  Destroy internal condition variable
1039
 
1040
      Must_Not_Fail (DosCloseEventSem (S.CV'Access));
1041
   end Finalize;
1042
 
1043
   -------------------
1044
   -- Current_State --
1045
   -------------------
1046
 
1047
   function Current_State (S : Suspension_Object) return Boolean is
1048
   begin
1049
      --  We do not want to use lock on this read operation. State is marked
1050
      --  as Atomic so that we ensure that the value retrieved is correct.
1051
 
1052
      return S.State;
1053
   end Current_State;
1054
 
1055
   ---------------
1056
   -- Set_False --
1057
   ---------------
1058
 
1059
   procedure Set_False (S : in out Suspension_Object) is
1060
   begin
1061
      Must_Not_Fail (DosRequestMutexSem (S.L, SEM_INDEFINITE_WAIT));
1062
 
1063
      S.State := False;
1064
 
1065
      Must_Not_Fail (DosReleaseMutexSem (S.L));
1066
   end Set_False;
1067
 
1068
   --------------
1069
   -- Set_True --
1070
   --------------
1071
 
1072
   procedure Set_True (S : in out Suspension_Object) is
1073
   begin
1074
      Must_Not_Fail (DosRequestMutexSem (S.L, SEM_INDEFINITE_WAIT));
1075
 
1076
      --  If there is already a task waiting on this suspension object then
1077
      --  we resume it, leaving the state of the suspension object to False,
1078
      --  as it is specified in ARM D.10 par. 9. Otherwise, it just leaves
1079
      --  the state to True.
1080
 
1081
      if S.Waiting then
1082
         S.Waiting := False;
1083
         S.State := False;
1084
 
1085
         Sem_Must_Not_Fail (DosPostEventSem (S.CV));
1086
      else
1087
         S.State := True;
1088
      end if;
1089
 
1090
      Must_Not_Fail (DosReleaseMutexSem (S.L));
1091
   end Set_True;
1092
 
1093
   ------------------------
1094
   -- Suspend_Until_True --
1095
   ------------------------
1096
 
1097
   procedure Suspend_Until_True (S : in out Suspension_Object) is
1098
      Count : aliased ULONG; -- Used to store dummy result
1099
   begin
1100
      Must_Not_Fail (DosRequestMutexSem (S.L, SEM_INDEFINITE_WAIT));
1101
 
1102
      if S.Waiting then
1103
         --  Program_Error must be raised upon calling Suspend_Until_True
1104
         --  if another task is already waiting on that suspension object
1105
         --  (ARM D.10 par. 10).
1106
 
1107
         Must_Not_Fail (DosReleaseMutexSem (S.L));
1108
 
1109
         raise Program_Error;
1110
      else
1111
         --  Suspend the task if the state is False. Otherwise, the task
1112
         --  continues its execution, and the state of the suspension object
1113
         --  is set to False (ARM D.10 par. 9).
1114
 
1115
         if S.State then
1116
            S.State := False;
1117
 
1118
            Must_Not_Fail (DosReleaseMutexSem (S.L));
1119
         else
1120
            S.Waiting := True;
1121
 
1122
            --  Must reset Cond BEFORE L is unlocked
1123
 
1124
            Sem_Must_Not_Fail
1125
              (DosResetEventSem (S.CV, Count'Unchecked_Access));
1126
 
1127
            Must_Not_Fail (DosReleaseMutexSem (S.L));
1128
 
1129
            Sem_Must_Not_Fail
1130
              (DosWaitEventSem (S.CV, SEM_INDEFINITE_WAIT));
1131
         end if;
1132
      end if;
1133
   end Suspend_Until_True;
1134
 
1135
   ----------------
1136
   -- Check_Exit --
1137
   ----------------
1138
 
1139
   --  Dummy version
1140
 
1141
   function Check_Exit (Self_ID : ST.Task_Id) return Boolean is
1142
   begin
1143
      return Check_No_Locks (Self_ID);
1144
   end Check_Exit;
1145
 
1146
   --------------------
1147
   -- Check_No_Locks --
1148
   --------------------
1149
 
1150
   function Check_No_Locks (Self_ID : ST.Task_Id) return Boolean is
1151
      TLD : constant Access_Thread_Local_Data := Thread_Local_Data_Ptr;
1152
   begin
1153
      return Self_ID = TLD.Self_ID
1154
        and then TLD.Lock_Prio_Level = 0;
1155
   end Check_No_Locks;
1156
 
1157
   ----------------------
1158
   -- Environment_Task --
1159
   ----------------------
1160
 
1161
   function Environment_Task return Task_Id is
1162
   begin
1163
      return Environment_Task_Id;
1164
   end Environment_Task;
1165
 
1166
   --------------
1167
   -- Lock_RTS --
1168
   --------------
1169
 
1170
   procedure Lock_RTS is
1171
   begin
1172
      Write_Lock (Single_RTS_Lock'Access, Global_Lock => True);
1173
   end Lock_RTS;
1174
 
1175
   ----------------
1176
   -- Unlock_RTS --
1177
   ----------------
1178
 
1179
   procedure Unlock_RTS is
1180
   begin
1181
      Unlock (Single_RTS_Lock'Access, Global_Lock => True);
1182
   end Unlock_RTS;
1183
 
1184
   ------------------
1185
   -- Suspend_Task --
1186
   ------------------
1187
 
1188
   function Suspend_Task
1189
     (T           : ST.Task_Id;
1190
      Thread_Self : Thread_Id) return Boolean
1191
   is
1192
   begin
1193
      if Thread_Id (T.Common.LL.Thread) /= Thread_Self then
1194
         return DosSuspendThread (T.Common.LL.Thread) = NO_ERROR;
1195
      else
1196
         return True;
1197
      end if;
1198
   end Suspend_Task;
1199
 
1200
   -----------------
1201
   -- Resume_Task --
1202
   -----------------
1203
 
1204
   function Resume_Task
1205
     (T           : ST.Task_Id;
1206
      Thread_Self : Thread_Id) return Boolean
1207
   is
1208
   begin
1209
      if Thread_Id (T.Common.LL.Thread) /= Thread_Self then
1210
         return DosResumeThread (T.Common.LL.Thread) = NO_ERROR;
1211
      else
1212
         return True;
1213
      end if;
1214
   end Resume_Task;
1215
 
1216
   ----------------
1217
   -- Initialize --
1218
   ----------------
1219
 
1220
   procedure Initialize (Environment_Task : Task_Id) is
1221
      Succeeded : Boolean;
1222
   begin
1223
      Environment_Task_Id := Environment_Task;
1224
 
1225
      OS_Primitives.Initialize;
1226
 
1227
      --  Initialize pointer to task local data.
1228
      --  This is done once, for all tasks.
1229
 
1230
      Must_Not_Fail (DosAllocThreadLocalMemory
1231
         ((Thread_Local_Data'Size + 31) / 32,  --  nr of 32-bit words
1232
          To_PPVOID (Thread_Local_Data_Ptr'Access)));
1233
 
1234
      --  Initialize thread local data for main thread
1235
 
1236
      Thread_Local_Data_Ptr.Self_ID := null;
1237
      Thread_Local_Data_Ptr.Lock_Prio_Level := 0;
1238
 
1239
      Initialize_Lock (Single_RTS_Lock'Access, RTS_Lock_Level);
1240
      --  Initialize the lock used to synchronize chain of all ATCBs
1241
 
1242
      --  Set ID of environment task
1243
 
1244
      Thread_Local_Data_Ptr.Self_ID := Environment_Task;
1245
      Environment_Task.Common.LL.Thread := 1; --  By definition
1246
 
1247
      --  This priority is unknown in fact.
1248
      --  If actual current priority is different,
1249
      --  it will get synchronized later on anyway.
1250
 
1251
      Environment_Task.Common.LL.Current_Priority :=
1252
        Environment_Task.Common.Current_Priority;
1253
 
1254
      --  Initialize TCB for this task.
1255
      --  This includes all the normal task-external initialization.
1256
      --  This is also done by Initialize_ATCB, why ???
1257
 
1258
      Initialize_TCB (Environment_Task, Succeeded);
1259
 
1260
      --  Consider raising Storage_Error,
1261
      --  if propagation can be tolerated ???
1262
 
1263
      pragma Assert (Succeeded);
1264
 
1265
      --  Do normal task-internal initialization,
1266
      --  which depends on an initialized TCB.
1267
 
1268
      Enter_Task (Environment_Task);
1269
 
1270
      --  Insert here any other special
1271
      --  initialization needed for the environment task.
1272
   end Initialize;
1273
 
1274
end System.Task_Primitives.Operations;

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.