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

Subversion Repositories openrisc

[/] [openrisc/] [trunk/] [rtos/] [ecos-2.0/] [doc/] [html/] [ref/] [kernel-mutexes.html] - Blame information for rev 174

Details | Compare with Previous | View Log

Line No. Rev Author Line
1 28 unneback
<!-- Copyright (C) 2003 Red Hat, Inc.                                -->
2
<!-- This material may be distributed only subject to the terms      -->
3
<!-- and conditions set forth in the Open Publication License, v1.0  -->
4
<!-- or later (the latest version is presently available at          -->
5
<!-- http://www.opencontent.org/openpub/).                           -->
6
<!-- Distribution of the work or derivative of the work in any       -->
7
<!-- standard (paper) book form is prohibited unless prior           -->
8
<!-- permission is obtained from the copyright holder.               -->
9
<HTML
10
><HEAD
11
><TITLE
12
>Mutexes</TITLE
13
><meta name="MSSmartTagsPreventParsing" content="TRUE">
14
<META
15
NAME="GENERATOR"
16
CONTENT="Modular DocBook HTML Stylesheet Version 1.76b+
17
"><LINK
18
REL="HOME"
19
TITLE="eCos Reference Manual"
20
HREF="ecos-ref.html"><LINK
21
REL="UP"
22
TITLE="The eCos Kernel"
23
HREF="kernel.html"><LINK
24
REL="PREVIOUS"
25
TITLE="Alarms"
26
HREF="kernel-alarms.html"><LINK
27
REL="NEXT"
28
TITLE="Condition Variables"
29
HREF="kernel-condition-variables.html"></HEAD
30
><BODY
31
CLASS="REFENTRY"
32
BGCOLOR="#FFFFFF"
33
TEXT="#000000"
34
LINK="#0000FF"
35
VLINK="#840084"
36
ALINK="#0000FF"
37
><DIV
38
CLASS="NAVHEADER"
39
><TABLE
40
SUMMARY="Header navigation table"
41
WIDTH="100%"
42
BORDER="0"
43
CELLPADDING="0"
44
CELLSPACING="0"
45
><TR
46
><TH
47
COLSPAN="3"
48
ALIGN="center"
49
>eCos Reference Manual</TH
50
></TR
51
><TR
52
><TD
53
WIDTH="10%"
54
ALIGN="left"
55
VALIGN="bottom"
56
><A
57
HREF="kernel-alarms.html"
58
ACCESSKEY="P"
59
>Prev</A
60
></TD
61
><TD
62
WIDTH="80%"
63
ALIGN="center"
64
VALIGN="bottom"
65
></TD
66
><TD
67
WIDTH="10%"
68
ALIGN="right"
69
VALIGN="bottom"
70
><A
71
HREF="kernel-condition-variables.html"
72
ACCESSKEY="N"
73
>Next</A
74
></TD
75
></TR
76
></TABLE
77
><HR
78
ALIGN="LEFT"
79
WIDTH="100%"></DIV
80
><H1
81
><A
82
NAME="KERNEL-MUTEXES">Mutexes</H1
83
><DIV
84
CLASS="REFNAMEDIV"
85
><A
86
NAME="AEN1098"
87
></A
88
><H2
89
>Name</H2
90
>cyg_mutex_init, cyg_mutex_destroy, cyg_mutex_lock, cyg_mutex_trylock, cyg_mutex_unlock, cyg_mutex_release, cyg_mutex_set_ceiling, cyg_mutex_set_protocol&nbsp;--&nbsp;Synchronization primitive</DIV
91
><DIV
92
CLASS="REFSYNOPSISDIV"
93
><A
94
NAME="AEN1108"><H2
95
>Synopsis</H2
96
><DIV
97
CLASS="FUNCSYNOPSIS"
98
><A
99
NAME="AEN1109"><P
100
></P
101
><TABLE
102
BORDER="5"
103
BGCOLOR="#E0E0F0"
104
WIDTH="70%"
105
><TR
106
><TD
107
><PRE
108
CLASS="FUNCSYNOPSISINFO"
109
>#include &lt;cyg/kernel/kapi.h&gt;
110
        </PRE
111
></TD
112
></TR
113
></TABLE
114
><P
115
><CODE
116
><CODE
117
CLASS="FUNCDEF"
118
>void cyg_mutex_init</CODE
119
>(cyg_mutex_t* mutex);</CODE
120
></P
121
><P
122
><CODE
123
><CODE
124
CLASS="FUNCDEF"
125
>void cyg_mutex_destroy</CODE
126
>(cyg_mutex_t* mutex);</CODE
127
></P
128
><P
129
><CODE
130
><CODE
131
CLASS="FUNCDEF"
132
>cyg_bool_t cyg_mutex_lock</CODE
133
>(cyg_mutex_t* mutex);</CODE
134
></P
135
><P
136
><CODE
137
><CODE
138
CLASS="FUNCDEF"
139
>cyg_bool_t cyg_mutex_trylock</CODE
140
>(cyg_mutex_t* mutex);</CODE
141
></P
142
><P
143
><CODE
144
><CODE
145
CLASS="FUNCDEF"
146
>void cyg_mutex_unlock</CODE
147
>(cyg_mutex_t* mutex);</CODE
148
></P
149
><P
150
><CODE
151
><CODE
152
CLASS="FUNCDEF"
153
>void cyg_mutex_release</CODE
154
>(cyg_mutex_t* mutex);</CODE
155
></P
156
><P
157
><CODE
158
><CODE
159
CLASS="FUNCDEF"
160
>void cyg_mutex_set_ceiling</CODE
161
>(cyg_mutex_t* mutex, cyg_priority_t priority);</CODE
162
></P
163
><P
164
><CODE
165
><CODE
166
CLASS="FUNCDEF"
167
>void cyg_mutex_set_protocol</CODE
168
>(cyg_mutex_t* mutex, enum cyg_mutex_protocol protocol/);</CODE
169
></P
170
><P
171
></P
172
></DIV
173
></DIV
174
><DIV
175
CLASS="REFSECT1"
176
><A
177
NAME="KERNEL-MUTEXES-DESCRIPTION"
178
></A
179
><H2
180
>Description</H2
181
><P
182
>The purpose of mutexes is to let threads share resources safely. If
183
two or more threads attempt to manipulate a data structure with no
184
locking between them then the system may run for quite some time
185
without apparent problems, but sooner or later the data structure will
186
become inconsistent and the application will start behaving strangely
187
and is quite likely to crash. The same can apply even when
188
manipulating a single variable or some other resource. For example,
189
consider:
190
      </P
191
><TABLE
192
BORDER="5"
193
BGCOLOR="#E0E0F0"
194
WIDTH="70%"
195
><TR
196
><TD
197
><PRE
198
CLASS="PROGRAMLISTING"
199
>static volatile int counter = 0;
200
 
201
void
202
process_event(void)
203
{
204
    &#8230;
205
 
206
    counter++;
207
}</PRE
208
></TD
209
></TR
210
></TABLE
211
><P
212
>Assume that after a certain period of time <TT
213
CLASS="VARNAME"
214
>counter</TT
215
>
216
has a value of 42, and two threads A and B running at the same
217
priority call <TT
218
CLASS="FUNCTION"
219
>process_event</TT
220
>. Typically thread A
221
will read the value of <TT
222
CLASS="VARNAME"
223
>counter</TT
224
> into a register,
225
increment this register to 43, and write this updated value back to
226
memory. Thread B will do the same, so usually
227
<TT
228
CLASS="VARNAME"
229
>counter</TT
230
> will end up with a value of 44. However if
231
thread A is timesliced after reading the old value 42 but before
232
writing back 43, thread B will still read back the old value and will
233
also write back 43. The net result is that the counter only gets
234
incremented once, not twice, which depending on the application may
235
prove disastrous.
236
      </P
237
><P
238
>Sections of code like the above which involve manipulating shared data
239
are generally known as critical regions. Code should claim a lock
240
before entering a critical region and release the lock when leaving.
241
Mutexes provide an appropriate synchronization primitive for this.
242
      </P
243
><TABLE
244
BORDER="5"
245
BGCOLOR="#E0E0F0"
246
WIDTH="70%"
247
><TR
248
><TD
249
><PRE
250
CLASS="PROGRAMLISTING"
251
>static volatile int counter = 0;
252
static cyg_mutex_t  lock;
253
 
254
void
255
process_event(void)
256
{
257
    &#8230;
258
 
259
    cyg_mutex_lock(&amp;lock);
260
    counter++;
261
    cyg_mutex_unlock(&amp;lock);
262
}
263
      </PRE
264
></TD
265
></TR
266
></TABLE
267
><P
268
>A mutex must be initialized before it can be used, by calling
269
<TT
270
CLASS="FUNCTION"
271
>cyg_mutex_init</TT
272
>. This takes a pointer to a
273
<SPAN
274
CLASS="STRUCTNAME"
275
>cyg_mutex_t</SPAN
276
> data structure which is typically
277
statically allocated, and may be part of a larger data structure. If a
278
mutex is no longer required and there are no threads waiting on it
279
then <TT
280
CLASS="FUNCTION"
281
>cyg_mutex_destroy</TT
282
> can be used.
283
      </P
284
><P
285
>The main functions for using a mutex are
286
<TT
287
CLASS="FUNCTION"
288
>cyg_mutex_lock</TT
289
> and
290
<TT
291
CLASS="FUNCTION"
292
>cyg_mutex_unlock</TT
293
>. In normal operation
294
<TT
295
CLASS="FUNCTION"
296
>cyg_mutex_lock</TT
297
> will return success after claiming
298
the mutex lock, blocking if another thread currently owns the mutex.
299
However the lock operation may fail if other code calls
300
<TT
301
CLASS="FUNCTION"
302
>cyg_mutex_release</TT
303
> or
304
<TT
305
CLASS="FUNCTION"
306
>cyg_thread_release</TT
307
>, so if these functions may get
308
used then it is important to check the return value. The current owner
309
of a mutex should call <TT
310
CLASS="FUNCTION"
311
>cyg_mutex_unlock</TT
312
> when a
313
lock is no longer required. This operation must be performed by the
314
owner, not by another thread.
315
      </P
316
><P
317
><TT
318
CLASS="FUNCTION"
319
>cyg_mutex_trylock</TT
320
> is a variant of
321
<TT
322
CLASS="FUNCTION"
323
>cyg_mutex_lock</TT
324
> that will always return
325
immediately, returning success or failure as appropriate. This
326
function is rarely useful. Typical code locks a mutex just before
327
entering a critical region, so if the lock cannot be claimed then
328
there may be nothing else for the current thread to do. Use of this
329
function may also cause a form of priority inversion if the owner
330
owner runs at a lower priority, because the priority inheritance code
331
will not be triggered. Instead the current thread continues running,
332
preventing the owner from getting any cpu time, completing the
333
critical region, and releasing the mutex.
334
      </P
335
><P
336
><TT
337
CLASS="FUNCTION"
338
>cyg_mutex_release</TT
339
> can be used to wake up all
340
threads that are currently blocked inside a call to
341
<TT
342
CLASS="FUNCTION"
343
>cyg_mutex_lock</TT
344
> for a specific mutex. These lock
345
calls will return failure. The current mutex owner is not affected.
346
      </P
347
></DIV
348
><DIV
349
CLASS="REFSECT1"
350
><A
351
NAME="KERNEL-MUTEXES-PRIORITY-INVERSION"
352
></A
353
><H2
354
>Priority Inversion</H2
355
><P
356
>The use of mutexes gives rise to a problem known as priority
357
inversion. In a typical scenario this requires three threads A, B, and
358
C, running at high, medium and low priority respectively. Thread A and
359
thread B are temporarily blocked waiting for some event, so thread C
360
gets a chance to run, needs to enter a critical region, and locks
361
a mutex. At this point threads A and B are woken up - the exact order
362
does not matter. Thread A needs to claim the same mutex but has to
363
wait until C has left the critical region and can release the mutex.
364
Meanwhile thread B works on something completely different and can
365
continue running without problems. Because thread C is running a lower
366
priority than B it will not get a chance to run until B blocks for
367
some reason, and hence thread A cannot run either. The overall effect
368
is that a high-priority thread A cannot proceed because of a lower
369
priority thread B, and priority inversion has occurred.
370
      </P
371
><P
372
>In simple applications it may be possible to arrange the code such
373
that priority inversion cannot occur, for example by ensuring that a
374
given mutex is never shared by threads running at different priority
375
levels. However this may not always be possible even at the
376
application level. In addition mutexes may be used internally by
377
underlying code, for example the memory allocation package, so careful
378
analysis of the whole system would be needed to be sure that priority
379
inversion cannot occur. Instead it is common practice to use one of
380
two techniques: priority ceilings and priority inheritance.
381
      </P
382
><P
383
>Priority ceilings involve associating a priority with each mutex.
384
Usually this will match the highest priority thread that will ever
385
lock the mutex. When a thread running at a lower priority makes a
386
successful call to <TT
387
CLASS="FUNCTION"
388
>cyg_mutex_lock</TT
389
> or
390
<TT
391
CLASS="FUNCTION"
392
>cyg_mutex_trylock</TT
393
> its priority will be boosted to
394
that of the mutex. For example, given the previous example the
395
priority associated with the mutex would be that of thread A, so for
396
as long as it owns the mutex thread C will run in preference to thread
397
B. When C releases the mutex its priority drops to the normal value
398
again, allowing A to run and claim the mutex. Setting the
399
priority for a mutex involves a call to
400
<TT
401
CLASS="FUNCTION"
402
>cyg_mutex_set_ceiling</TT
403
>, which is typically called
404
during initialization. It is possible to change the ceiling
405
dynamically but this will only affect subsequent lock operations, not
406
the current owner of the mutex.
407
      </P
408
><P
409
>Priority ceilings are very suitable for simple applications, where for
410
every thread in the system it is possible to work out which mutexes
411
will be accessed. For more complicated applications this may prove
412
difficult, especially if thread priorities change at run-time. An
413
additional problem occurs for any mutexes outside the application, for
414
example used internally within eCos packages. A typical eCos package
415
will be unaware of the details of the various threads in the system,
416
so it will have no way of setting suitable ceilings for its internal
417
mutexes. If those mutexes are not exported to application code then
418
using priority ceilings may not be viable. The kernel does provide a
419
configuration option
420
<TT
421
CLASS="VARNAME"
422
>CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT_PRIORITY</TT
423
>
424
that can be used to set the default priority ceiling for all mutexes,
425
which may prove sufficient.
426
      </P
427
><P
428
>The alternative approach is to use priority inheritance: if a thread
429
calls <TT
430
CLASS="FUNCTION"
431
>cyg_mutex_lock</TT
432
> for a mutex that it
433
currently owned by a lower-priority thread, then the owner will have
434
its priority raised to that of the current thread. Often this is more
435
efficient than priority ceilings because priority boosting only
436
happens when necessary, not for every lock operation, and the required
437
priority is determined at run-time rather than by static analysis.
438
However there are complications when multiple threads running at
439
different priorities try to lock a single mutex, or when the current
440
owner of a mutex then tries to lock additional mutexes, and this makes
441
the implementation significantly more complicated than priority
442
ceilings.
443
      </P
444
><P
445
>There are a number of configuration options associated with priority
446
inversion. First, if after careful analysis it is known that priority
447
inversion cannot arise then the component
448
<TT
449
CLASS="FUNCTION"
450
>CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL</TT
451
>
452
can be disabled. More commonly this component will be enabled, and one
453
of either
454
<TT
455
CLASS="VARNAME"
456
>CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_INHERIT</TT
457
>
458
or
459
<TT
460
CLASS="VARNAME"
461
>CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_CEILING</TT
462
>
463
will be selected, so that one of the two protocols is available for
464
all mutexes. It is possible to select multiple protocols, so that some
465
mutexes can have priority ceilings while others use priority
466
inheritance or no priority inversion protection at all. Obviously this
467
flexibility will add to the code size and to the cost of mutex
468
operations. The default for all mutexes will be controlled by
469
<TT
470
CLASS="VARNAME"
471
>CYGSEM_KERNEL_SYNCH_MUTEX_PRIORITY_INVERSION_PROTOCOL_DEFAULT</TT
472
>,
473
and can be changed at run-time using
474
<TT
475
CLASS="FUNCTION"
476
>cyg_mutex_set_protocol</TT
477
>.
478
      </P
479
><P
480
>Priority inversion problems can also occur with other synchronization
481
primitives such as semaphores. For example there could be a situation
482
where a high-priority thread A is waiting on a semaphore, a
483
low-priority thread C needs to do just a little bit more work before
484
posting the semaphore, but a medium priority thread B is running and
485
preventing C from making progress. However a semaphore does not have
486
the concept of an owner, so there is no way for the system to know
487
that it is thread C which would next post to the semaphore. Hence
488
there is no way for the system to boost the priority of C
489
automatically and prevent the priority inversion. Instead situations
490
like this have to be detected by application developers and
491
appropriate precautions have to be taken, for example making sure that
492
all the threads run at suitable priorities at all times.
493
      </P
494
><DIV
495
CLASS="WARNING"
496
><P
497
></P
498
><TABLE
499
CLASS="WARNING"
500
BORDER="1"
501
WIDTH="100%"
502
><TR
503
><TD
504
ALIGN="CENTER"
505
><B
506
>Warning</B
507
></TD
508
></TR
509
><TR
510
><TD
511
ALIGN="LEFT"
512
><P
513
>The current implementation of priority inheritance within the eCos
514
kernel does not handle certain exceptional circumstances completely
515
correctly. Problems will only arise if a thread owns one mutex,
516
then attempts to claim another mutex, and there are other threads
517
attempting to lock these same mutexes. Although the system will
518
continue running, the current owners of the various mutexes involved
519
may not run at the priority they should. This situation never arises
520
in typical code because a mutex will only be locked for a small
521
critical region, and there is no need to manipulate other shared resources
522
inside this region. A more complicated implementation of priority
523
inheritance is possible but would add significant overhead and certain
524
operations would no longer be deterministic.
525
      </P
526
></TD
527
></TR
528
></TABLE
529
></DIV
530
><DIV
531
CLASS="WARNING"
532
><P
533
></P
534
><TABLE
535
CLASS="WARNING"
536
BORDER="1"
537
WIDTH="100%"
538
><TR
539
><TD
540
ALIGN="CENTER"
541
><B
542
>Warning</B
543
></TD
544
></TR
545
><TR
546
><TD
547
ALIGN="LEFT"
548
><P
549
>Support for priority ceilings and priority inheritance is not
550
implemented for all schedulers. In particular neither priority
551
ceilings nor priority inheritance are currently available for the
552
bitmap scheduler.
553
      </P
554
></TD
555
></TR
556
></TABLE
557
></DIV
558
></DIV
559
><DIV
560
CLASS="REFSECT1"
561
><A
562
NAME="KERNEL-MUTEXES-ALTERNATIVES"
563
></A
564
><H2
565
>Alternatives</H2
566
><P
567
>In nearly all circumstances, if two or more threads need to share some
568
data then protecting this data with a mutex is the correct thing to
569
do. Mutexes are the only primitive that combine a locking mechanism
570
and protection against priority inversion problems. However this
571
functionality is achieved at a cost, and in exceptional circumstances
572
such as an application's most critical inner loop it may be desirable
573
to use some other means of locking.
574
      </P
575
><P
576
>When a critical region is very very small it is possible to lock the
577
scheduler, thus ensuring that no other thread can run until the
578
scheduler is unlocked again. This is achieved with calls to <A
579
HREF="kernel-schedcontrol.html"
580
><TT
581
CLASS="FUNCTION"
582
>cyg_scheduler_lock</TT
583
></A
584
>
585
and <TT
586
CLASS="FUNCTION"
587
>cyg_scheduler_unlock</TT
588
>. If the critical region
589
is sufficiently small then this can actually improve both performance
590
and dispatch latency because <TT
591
CLASS="FUNCTION"
592
>cyg_mutex_lock</TT
593
> also
594
locks the scheduler for a brief period of time. This approach will not
595
work on SMP systems because another thread may already be running on a
596
different processor and accessing the critical region.
597
      </P
598
><P
599
>Another way of avoiding the use of mutexes is to make sure that all
600
threads that access a particular critical region run at the same
601
priority and configure the system with timeslicing disabled
602
(<TT
603
CLASS="VARNAME"
604
>CYGSEM_KERNEL_SCHED_TIMESLICE</TT
605
>). Without
606
timeslicing a thread can only be preempted by a higher-priority one,
607
or if it performs some operation that can block. This approach
608
requires that none of the operations in the critical region can block,
609
so for example it is not legal to call
610
<TT
611
CLASS="FUNCTION"
612
>cyg_semaphore_wait</TT
613
>. It is also vulnerable to
614
any changes in the configuration or to the various thread priorities:
615
any such changes may now have unexpected side effects. It will not
616
work on SMP systems.
617
      </P
618
></DIV
619
><DIV
620
CLASS="REFSECT1"
621
><A
622
NAME="KERNEL-MUTEXES-RECURSIVE"
623
></A
624
><H2
625
>Recursive Mutexes</H2
626
><P
627
>The implementation of mutexes within the eCos kernel does not support
628
recursive locks. If a thread has locked a mutex and then attempts to
629
lock the mutex again, typically as a result of some recursive call in
630
a complicated call graph, then either an assertion failure will be
631
reported or the thread will deadlock. This behaviour is deliberate.
632
When a thread has just locked a mutex associated with some data
633
structure, it can assume that that data structure is in a consistent
634
state. Before unlocking the mutex again it must ensure that the data
635
structure is again in a consistent state. Recursive mutexes allow a
636
thread to make arbitrary changes to a data structure, then in a
637
recursive call lock the mutex again while the data structure is still
638
inconsistent. The net result is that code can no longer make any
639
assumptions about data structure consistency, which defeats the
640
purpose of using mutexes.
641
      </P
642
></DIV
643
><DIV
644
CLASS="REFSECT1"
645
><A
646
NAME="KERNEL-MUTEXES-CONTEXT"
647
></A
648
><H2
649
>Valid contexts</H2
650
><P
651
><TT
652
CLASS="FUNCTION"
653
>cyg_mutex_init</TT
654
>,
655
<TT
656
CLASS="FUNCTION"
657
>cyg_mutex_set_ceiling</TT
658
> and
659
<TT
660
CLASS="FUNCTION"
661
>cyg_mutex_set_protocol</TT
662
> are normally called during
663
initialization but may also be called from thread context. The
664
remaining functions should only be called from thread context. Mutexes
665
serve as a mutual exclusion mechanism between threads, and cannot be
666
used to synchronize between threads and the interrupt handling
667
subsystem. If a critical region is shared between a thread and a DSR
668
then it must be protected using <A
669
HREF="kernel-schedcontrol.html"
670
><TT
671
CLASS="FUNCTION"
672
>cyg_scheduler_lock</TT
673
></A
674
>
675
and <TT
676
CLASS="FUNCTION"
677
>cyg_scheduler_unlock</TT
678
>. If a critical region is
679
shared between a thread and an ISR, it must be protected by disabling
680
or masking interrupts. Obviously these operations must be used with
681
care because they can affect dispatch and interrupt latencies.
682
      </P
683
></DIV
684
><DIV
685
CLASS="NAVFOOTER"
686
><HR
687
ALIGN="LEFT"
688
WIDTH="100%"><TABLE
689
SUMMARY="Footer navigation table"
690
WIDTH="100%"
691
BORDER="0"
692
CELLPADDING="0"
693
CELLSPACING="0"
694
><TR
695
><TD
696
WIDTH="33%"
697
ALIGN="left"
698
VALIGN="top"
699
><A
700
HREF="kernel-alarms.html"
701
ACCESSKEY="P"
702
>Prev</A
703
></TD
704
><TD
705
WIDTH="34%"
706
ALIGN="center"
707
VALIGN="top"
708
><A
709
HREF="ecos-ref.html"
710
ACCESSKEY="H"
711
>Home</A
712
></TD
713
><TD
714
WIDTH="33%"
715
ALIGN="right"
716
VALIGN="top"
717
><A
718
HREF="kernel-condition-variables.html"
719
ACCESSKEY="N"
720
>Next</A
721
></TD
722
></TR
723
><TR
724
><TD
725
WIDTH="33%"
726
ALIGN="left"
727
VALIGN="top"
728
>Alarms</TD
729
><TD
730
WIDTH="34%"
731
ALIGN="center"
732
VALIGN="top"
733
><A
734
HREF="kernel.html"
735
ACCESSKEY="U"
736
>Up</A
737
></TD
738
><TD
739
WIDTH="33%"
740
ALIGN="right"
741
VALIGN="top"
742
>Condition Variables</TD
743
></TR
744
></TABLE
745
></DIV
746
></BODY
747
></HTML
748
>

powered by: WebSVN 2.1.0

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