source: trunk/src/odincrt/critsect.cpp@ 9807

Last change on this file since 9807 was 9756, checked in by sandervl, 23 years ago

Rewrote critical sections to be safe on SMP systems

File size: 6.2 KB
Line 
1/* $Id: critsect.cpp,v 1.8 2003-02-06 10:30:02 sandervl Exp $ */
2/*
3 * Critical sections in the Win32 sense
4 *
5 * Copyright 2002 Sander van Leeuwen <sandervl@innotek.de>
6 *
7 */
8#define INCL_DOSPROCESS
9#define INCL_DOSERRORS
10#define INCL_DOSSEMAPHORES
11#include <os2wrap.h>
12#include <win32type.h>
13#include <win32api.h>
14
15#include <assert.h>
16#include <stdio.h>
17
18#include <odincrt.h>
19
20#ifdef DEBUG
21#define DebugInt3() _interrupt(3)
22#else
23#define DebugInt3()
24#endif
25
26//******************************************************************************
27// This is an OS/2 implementation of what Win32 treats as "critical sections"
28// It is an implementation that is highly optimized for the case where there is
29// only one thread trying to access the critical section, i.e. it is available
30// most of the time. Therefore we can use these critical sections for all our
31// serialization and not lose any performance when concurrent access is unlikely.
32//
33// In case there is multiple access, we use the OS/2 kernel event semaphores.
34//******************************************************************************
35
36
37// encode PID and TID into one 32bit value
38#define MAKE_THREADID(processid, threadid) ((processid << 16) | threadid)
39
40//******************************************************************************
41//******************************************************************************
42inline ULONG GetCurrentThreadId()
43{
44 PTIB ptib;
45 PPIB ppib;
46 APIRET rc;
47
48 rc = DosGetInfoBlocks(&ptib, &ppib);
49 if(rc == NO_ERROR) {
50#ifdef DEBUG
51 if(MAKE_THREADID(ppib->pib_ulpid, ptib->tib_ptib2->tib2_ultid) == 0) {
52 DebugInt3();
53 }
54#endif
55 return MAKE_THREADID(ppib->pib_ulpid, ptib->tib_ptib2->tib2_ultid);
56 }
57 DebugInt3();
58 return 0;
59}
60//******************************************************************************
61//******************************************************************************
62inline ULONG GetCurrentProcessId()
63{
64 PTIB ptib;
65 PPIB ppib;
66 APIRET rc;
67
68 rc = DosGetInfoBlocks(&ptib, &ppib);
69 if(rc == NO_ERROR) {
70 return ppib->pib_ulpid;
71 }
72 DebugInt3();
73 return 0;
74}
75
76/***********************************************************************
77 * DosInitializeCriticalSection
78 */
79ULONG WIN32API DosInitializeCriticalSection(CRITICAL_SECTION_OS2 *crit,
80 PSZ pszSemName, BOOL fShared)
81{
82 APIRET rc;
83
84 // initialize lock count with special value -1, meaning noone posesses it
85 crit->LockCount = -1;
86 crit->RecursionCount = 0;
87 crit->OwningThread = 0;
88
89 rc = DosCreateEventSem(pszSemName, &crit->hmtxLock, (pszSemName || fShared) ? DC_SEM_SHARED : 0, 0);
90 if(rc != NO_ERROR) {
91 DebugInt3();
92 crit->hmtxLock = 0;
93 return rc;
94 }
95 crit->CreationCount = 1;
96 crit->Reserved = GetCurrentProcessId();
97 return NO_ERROR;
98}
99
100
101/***********************************************************************
102 * DosAccessCriticalSection
103 */
104ULONG WIN32API DosAccessCriticalSection(CRITICAL_SECTION_OS2 *crit, PSZ pszSemName)
105{
106 HMTX hmtxLock = 0;
107 APIRET rc;
108
109 if(pszSemName == NULL && crit->hmtxLock == 0) {
110 DebugInt3();
111 return ERROR_INVALID_PARAMETER;
112 }
113 if(pszSemName == NULL) {
114 hmtxLock = crit->hmtxLock;
115 }
116
117 rc = DosOpenEventSem(pszSemName, &hmtxLock);
118 if(rc != NO_ERROR) {
119 DebugInt3();
120 return rc;
121 }
122 DosInterlockedIncrement(&crit->CreationCount);
123 return NO_ERROR;
124}
125/***********************************************************************
126 * DosDeleteCriticalSection
127 */
128ULONG WIN32API DosDeleteCriticalSection( CRITICAL_SECTION_OS2 *crit )
129{
130 if (crit->hmtxLock)
131 {
132#ifdef DEBUG
133 if (crit->LockCount != -1 || crit->OwningThread || crit->RecursionCount) /* Should not happen */
134 {
135 DebugInt3();
136 }
137#endif
138 DosCloseEventSem(crit->hmtxLock);
139 if(DosInterlockedDecrement(&crit->CreationCount) == 0)
140 {
141 crit->LockCount = -1;
142 crit->RecursionCount = 0;
143 crit->OwningThread = 0;
144 crit->hmtxLock = 0;
145 crit->Reserved = (DWORD)-1;
146 }
147 }
148 return NO_ERROR;
149}
150
151
152/***********************************************************************
153 * DosEnterCriticalSection
154 */
155ULONG WIN32API DosEnterCriticalSection( CRITICAL_SECTION_OS2 *crit, ULONG ulTimeout )
156{
157 DWORD res;
158 DWORD threadid = GetCurrentThreadId();
159
160 // create crit sect just in time...
161 if (!crit->hmtxLock)
162 {
163 DosInitializeCriticalSection(crit, NULL);
164 }
165 // if the same thread is requesting it again, memorize it
166 if (crit->OwningThread == threadid)
167 {
168 crit->RecursionCount++;
169 return NO_ERROR;
170 }
171
172 // do an atomic increase of the lockcounter
173 DosInterlockedIncrement(&crit->LockCount);
174
175 // do an atomic operation where we compare the owning thread id with 0
176 // and if this is true, exchange it with the id of the current thread.
177testenter:
178 if(DosInterlockedCompareExchange((PLONG)&crit->OwningThread, threadid, 0))
179 {
180 // the crit sect is in use
181 ULONG ulnrposts;
182
183 // now wait for it
184 APIRET rc = DosWaitEventSem(crit->hmtxLock, ulTimeout);
185 if(rc != NO_ERROR) {
186 DebugInt3();
187 return rc;
188 }
189 DosResetEventSem(crit->hmtxLock, &ulnrposts);
190 // multiple waiters could be running now. Repeat the logic so that
191 // only one actually can get the critical section
192 goto testenter;
193 }
194 crit->RecursionCount = 1;
195 return NO_ERROR;
196}
197
198
199/***********************************************************************
200 * DosLeaveCriticalSection
201 */
202ULONG WIN32API DosLeaveCriticalSection( CRITICAL_SECTION_OS2 *crit )
203{
204 if (crit->OwningThread != GetCurrentThreadId()) {
205 DebugInt3();
206 return ERROR_INVALID_PARAMETER;
207 }
208
209 if (--crit->RecursionCount)
210 {
211 //just return
212 return NO_ERROR;
213 }
214 crit->OwningThread = 0;
215 if (DosInterlockedDecrement( &crit->LockCount ) >= 0)
216 {
217 /* Someone is waiting */
218 DosPostEventSem(crit->hmtxLock);
219 }
220 return NO_ERROR;
221}
Note: See TracBrowser for help on using the repository browser.