1 /* This Source Code Form is subject to the terms of the Mozilla Public
2 * License, v. 2.0. If a copy of the MPL was not distributed with this
3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 /* $Id: sslmutex.c,v 1.28 2012/04/25 14:50:12 gerv%gerv.net Exp $ */
7 /* This ifdef should match the one in sslsnce.c */
8 #if defined(XP_UNIX) || defined(XP_WIN32) || defined (XP_OS2) || defined(XP_BEOS)
13 static SECStatus
single_process_sslMutex_Init(sslMutex
* pMutex
)
15 PR_ASSERT(pMutex
!= 0 && pMutex
->u
.sslLock
== 0 );
17 pMutex
->u
.sslLock
= PR_NewLock();
18 if (!pMutex
->u
.sslLock
) {
24 static SECStatus
single_process_sslMutex_Destroy(sslMutex
* pMutex
)
26 PR_ASSERT(pMutex
!= 0);
27 PR_ASSERT(pMutex
->u
.sslLock
!= 0);
28 if (!pMutex
->u
.sslLock
) {
29 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
32 PR_DestroyLock(pMutex
->u
.sslLock
);
36 static SECStatus
single_process_sslMutex_Unlock(sslMutex
* pMutex
)
38 PR_ASSERT(pMutex
!= 0 );
39 PR_ASSERT(pMutex
->u
.sslLock
!=0);
40 if (!pMutex
->u
.sslLock
) {
41 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
44 PR_Unlock(pMutex
->u
.sslLock
);
48 static SECStatus
single_process_sslMutex_Lock(sslMutex
* pMutex
)
50 PR_ASSERT(pMutex
!= 0);
51 PR_ASSERT(pMutex
->u
.sslLock
!= 0 );
52 if (!pMutex
->u
.sslLock
) {
53 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
56 PR_Lock(pMutex
->u
.sslLock
);
60 #if defined(LINUX) || defined(AIX) || defined(BEOS) || defined(BSDI) || (defined(NETBSD) && __NetBSD_Version__ < 500000000) || defined(OPENBSD)
69 #define SSL_MUTEX_MAGIC 0xfeedfd
70 #define NONBLOCKING_POSTS 1 /* maybe this is faster */
75 #define FNONBLOCK O_NONBLOCK
79 setNonBlocking(int fd
, int nonBlocking
)
84 flags
= fcntl(fd
, F_GETFL
, 0);
91 err
= fcntl(fd
, F_SETFL
, flags
);
97 sslMutex_Init(sslMutex
*pMutex
, int shared
)
101 pMutex
->isMultiProcess
= (PRBool
)(shared
!= 0);
103 return single_process_sslMutex_Init(pMutex
);
105 pMutex
->u
.pipeStr
.mPipes
[0] = -1;
106 pMutex
->u
.pipeStr
.mPipes
[1] = -1;
107 pMutex
->u
.pipeStr
.mPipes
[2] = -1;
108 pMutex
->u
.pipeStr
.nWaiters
= 0;
110 err
= pipe(pMutex
->u
.pipeStr
.mPipes
);
112 nss_MD_unix_map_default_error(errno
);
115 #if NONBLOCKING_POSTS
116 err
= setNonBlocking(pMutex
->u
.pipeStr
.mPipes
[1], 1);
121 pMutex
->u
.pipeStr
.mPipes
[2] = SSL_MUTEX_MAGIC
;
123 #if defined(LINUX) && defined(i386)
124 /* Pipe starts out empty */
127 /* Pipe starts with one byte. */
128 return sslMutex_Unlock(pMutex
);
132 nss_MD_unix_map_default_error(errno
);
133 close(pMutex
->u
.pipeStr
.mPipes
[0]);
134 close(pMutex
->u
.pipeStr
.mPipes
[1]);
139 sslMutex_Destroy(sslMutex
*pMutex
, PRBool processLocal
)
141 if (PR_FALSE
== pMutex
->isMultiProcess
) {
142 return single_process_sslMutex_Destroy(pMutex
);
144 if (pMutex
->u
.pipeStr
.mPipes
[2] != SSL_MUTEX_MAGIC
) {
145 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
148 close(pMutex
->u
.pipeStr
.mPipes
[0]);
149 close(pMutex
->u
.pipeStr
.mPipes
[1]);
155 pMutex
->u
.pipeStr
.mPipes
[0] = -1;
156 pMutex
->u
.pipeStr
.mPipes
[1] = -1;
157 pMutex
->u
.pipeStr
.mPipes
[2] = -1;
158 pMutex
->u
.pipeStr
.nWaiters
= 0;
163 #if defined(LINUX) && defined(i386)
164 /* No memory barrier needed for this platform */
166 /* nWaiters includes the holder of the lock (if any) and the number
167 ** threads waiting for it. After incrementing nWaiters, if the count
168 ** is exactly 1, then you have the lock and may proceed. If the
169 ** count is greater than 1, then you must wait on the pipe.
174 sslMutex_Unlock(sslMutex
*pMutex
)
177 if (PR_FALSE
== pMutex
->isMultiProcess
) {
178 return single_process_sslMutex_Unlock(pMutex
);
181 if (pMutex
->u
.pipeStr
.mPipes
[2] != SSL_MUTEX_MAGIC
) {
182 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
185 /* Do Memory Barrier here. */
186 newValue
= PR_ATOMIC_DECREMENT(&pMutex
->u
.pipeStr
.nWaiters
);
191 cc
= write(pMutex
->u
.pipeStr
.mPipes
[1], &c
, 1);
192 } while (cc
< 0 && (errno
== EINTR
|| errno
== EAGAIN
));
195 nss_MD_unix_map_default_error(errno
);
197 PORT_SetError(PR_UNKNOWN_ERROR
);
205 sslMutex_Lock(sslMutex
*pMutex
)
208 if (PR_FALSE
== pMutex
->isMultiProcess
) {
209 return single_process_sslMutex_Lock(pMutex
);
212 if (pMutex
->u
.pipeStr
.mPipes
[2] != SSL_MUTEX_MAGIC
) {
213 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
216 newValue
= PR_ATOMIC_INCREMENT(&pMutex
->u
.pipeStr
.nWaiters
);
217 /* Do Memory Barrier here. */
222 cc
= read(pMutex
->u
.pipeStr
.mPipes
[0], &c
, 1);
223 } while (cc
< 0 && errno
== EINTR
);
226 nss_MD_unix_map_default_error(errno
);
228 PORT_SetError(PR_UNKNOWN_ERROR
);
237 /* Using Atomic operations requires the use of a memory barrier instruction
238 ** on PowerPC, Sparc, and Alpha. NSPR's PR_Atomic functions do not perform
239 ** them, and NSPR does not provide a function that does them (e.g. PR_Barrier).
240 ** So, we don't use them on those platforms.
244 sslMutex_Unlock(sslMutex
*pMutex
)
249 if (PR_FALSE
== pMutex
->isMultiProcess
) {
250 return single_process_sslMutex_Unlock(pMutex
);
253 if (pMutex
->u
.pipeStr
.mPipes
[2] != SSL_MUTEX_MAGIC
) {
254 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
258 cc
= write(pMutex
->u
.pipeStr
.mPipes
[1], &c
, 1);
259 } while (cc
< 0 && (errno
== EINTR
|| errno
== EAGAIN
));
262 nss_MD_unix_map_default_error(errno
);
264 PORT_SetError(PR_UNKNOWN_ERROR
);
272 sslMutex_Lock(sslMutex
*pMutex
)
277 if (PR_FALSE
== pMutex
->isMultiProcess
) {
278 return single_process_sslMutex_Lock(pMutex
);
281 if (pMutex
->u
.pipeStr
.mPipes
[2] != SSL_MUTEX_MAGIC
) {
282 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
287 cc
= read(pMutex
->u
.pipeStr
.mPipes
[0], &c
, 1);
288 } while (cc
< 0 && errno
== EINTR
);
291 nss_MD_unix_map_default_error(errno
);
293 PORT_SetError(PR_UNKNOWN_ERROR
);
304 #include "win32err.h"
306 /* on Windows, we need to find the optimal type of locking mechanism to use
310 1) single-process, use a PRLock, as for all other platforms
311 2) Win95 multi-process, use a Win32 mutex
312 3) on WINNT multi-process, use a PRLock + a Win32 mutex
318 SECStatus
sslMutex_2LevelInit(sslMutex
*sem
)
320 /* the following adds a PRLock to sslMutex . This is done in each
321 process of a multi-process server and is only needed on WINNT, if
322 using fibers. We can't tell if native threads or fibers are used, so
323 we always do it on WINNT
327 /* we need to reset the sslLock in the children or the single_process init
328 function below will assert */
329 sem
->u
.sslLock
= NULL
;
331 return single_process_sslMutex_Init(sem
);
334 static SECStatus
sslMutex_2LevelDestroy(sslMutex
*sem
)
336 return single_process_sslMutex_Destroy(sem
);
342 sslMutex_Init(sslMutex
*pMutex
, int shared
)
348 SECURITY_ATTRIBUTES attributes
=
349 { sizeof(SECURITY_ATTRIBUTES
), NULL
, TRUE
};
351 PR_ASSERT(pMutex
!= 0 && (pMutex
->u
.sslMutx
== 0 ||
352 pMutex
->u
.sslMutx
== INVALID_HANDLE_VALUE
) );
354 pMutex
->isMultiProcess
= (PRBool
)(shared
!= 0);
356 if (PR_FALSE
== pMutex
->isMultiProcess
) {
357 return single_process_sslMutex_Init(pMutex
);
361 /* we need a lock on WINNT for fibers in the parent process */
362 retvalue
= sslMutex_2LevelInit(pMutex
);
363 if (SECSuccess
!= retvalue
)
367 if (!pMutex
|| ((hMutex
= pMutex
->u
.sslMutx
) != 0 &&
368 hMutex
!= INVALID_HANDLE_VALUE
)) {
369 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
372 attributes
.bInheritHandle
= (shared
? TRUE
: FALSE
);
373 hMutex
= CreateMutex(&attributes
, FALSE
, NULL
);
374 if (hMutex
== NULL
) {
375 hMutex
= INVALID_HANDLE_VALUE
;
376 nss_MD_win32_map_default_error(GetLastError());
379 pMutex
->u
.sslMutx
= hMutex
;
384 sslMutex_Destroy(sslMutex
*pMutex
, PRBool processLocal
)
388 int retvalue
= SECSuccess
;
390 PR_ASSERT(pMutex
!= 0);
391 if (PR_FALSE
== pMutex
->isMultiProcess
) {
392 return single_process_sslMutex_Destroy(pMutex
);
395 /* multi-process mode */
397 /* on NT, get rid of the PRLock used for fibers within a process */
398 retvalue
= sslMutex_2LevelDestroy(pMutex
);
401 PR_ASSERT( pMutex
->u
.sslMutx
!= 0 &&
402 pMutex
->u
.sslMutx
!= INVALID_HANDLE_VALUE
);
403 if (!pMutex
|| (hMutex
= pMutex
->u
.sslMutx
) == 0
404 || hMutex
== INVALID_HANDLE_VALUE
) {
405 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
409 rv
= CloseHandle(hMutex
); /* ignore error */
410 if (!processLocal
&& rv
) {
411 pMutex
->u
.sslMutx
= hMutex
= INVALID_HANDLE_VALUE
;
414 nss_MD_win32_map_default_error(GetLastError());
415 retvalue
= SECFailure
;
421 sslMutex_Unlock(sslMutex
*pMutex
)
423 BOOL success
= FALSE
;
426 PR_ASSERT(pMutex
!= 0 );
427 if (PR_FALSE
== pMutex
->isMultiProcess
) {
428 return single_process_sslMutex_Unlock(pMutex
);
431 PR_ASSERT(pMutex
->u
.sslMutx
!= 0 &&
432 pMutex
->u
.sslMutx
!= INVALID_HANDLE_VALUE
);
433 if (!pMutex
|| (hMutex
= pMutex
->u
.sslMutx
) == 0 ||
434 hMutex
== INVALID_HANDLE_VALUE
) {
435 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
438 success
= ReleaseMutex(hMutex
);
440 nss_MD_win32_map_default_error(GetLastError());
444 return single_process_sslMutex_Unlock(pMutex
);
445 /* release PRLock for other fibers in the process */
452 sslMutex_Lock(sslMutex
*pMutex
)
458 SECStatus retvalue
= SECSuccess
;
459 PR_ASSERT(pMutex
!= 0);
461 if (PR_FALSE
== pMutex
->isMultiProcess
) {
462 return single_process_sslMutex_Lock(pMutex
);
465 /* lock first to preserve from other threads/fibers
466 in the same process */
467 retvalue
= single_process_sslMutex_Lock(pMutex
);
469 PR_ASSERT(pMutex
->u
.sslMutx
!= 0 &&
470 pMutex
->u
.sslMutx
!= INVALID_HANDLE_VALUE
);
471 if (!pMutex
|| (hMutex
= pMutex
->u
.sslMutx
) == 0 ||
472 hMutex
== INVALID_HANDLE_VALUE
) {
473 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
474 return SECFailure
; /* what else ? */
476 /* acquire the mutex to be the only owner accross all other processes */
477 event
= WaitForSingleObject(hMutex
, INFINITE
);
485 #if defined(WAIT_IO_COMPLETION)
486 case WAIT_IO_COMPLETION
:
488 default: /* should never happen. nothing we can do. */
489 PR_ASSERT(!("WaitForSingleObject returned invalid value."));
490 PORT_SetError(PR_UNKNOWN_ERROR
);
494 case WAIT_FAILED
: /* failure returns this */
496 lastError
= GetLastError(); /* for debugging */
497 nss_MD_win32_map_default_error(lastError
);
501 if (! (SECSuccess
== retvalue
&& SECSuccess
== rv
)) {
508 #elif defined(XP_UNIX)
511 #include "unix_err.h"
514 sslMutex_Init(sslMutex
*pMutex
, int shared
)
518 pMutex
->isMultiProcess
= (PRBool
)(shared
!= 0);
520 return single_process_sslMutex_Init(pMutex
);
523 rv
= sem_init(&pMutex
->u
.sem
, shared
, 1);
524 } while (rv
< 0 && errno
== EINTR
);
526 nss_MD_unix_map_default_error(errno
);
533 sslMutex_Destroy(sslMutex
*pMutex
, PRBool processLocal
)
536 if (PR_FALSE
== pMutex
->isMultiProcess
) {
537 return single_process_sslMutex_Destroy(pMutex
);
540 /* semaphores are global resources. See SEM_DESTROY(3) man page */
545 rv
= sem_destroy(&pMutex
->u
.sem
);
546 } while (rv
< 0 && errno
== EINTR
);
548 nss_MD_unix_map_default_error(errno
);
555 sslMutex_Unlock(sslMutex
*pMutex
)
558 if (PR_FALSE
== pMutex
->isMultiProcess
) {
559 return single_process_sslMutex_Unlock(pMutex
);
562 rv
= sem_post(&pMutex
->u
.sem
);
563 } while (rv
< 0 && errno
== EINTR
);
565 nss_MD_unix_map_default_error(errno
);
572 sslMutex_Lock(sslMutex
*pMutex
)
575 if (PR_FALSE
== pMutex
->isMultiProcess
) {
576 return single_process_sslMutex_Lock(pMutex
);
579 rv
= sem_wait(&pMutex
->u
.sem
);
580 } while (rv
< 0 && errno
== EINTR
);
582 nss_MD_unix_map_default_error(errno
);
591 sslMutex_Init(sslMutex
*pMutex
, int shared
)
594 pMutex
->isMultiProcess
= (PRBool
)(shared
!= 0);
596 return single_process_sslMutex_Init(pMutex
);
598 PORT_Assert(!("sslMutex_Init not implemented for multi-process applications !"));
599 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
604 sslMutex_Destroy(sslMutex
*pMutex
, PRBool processLocal
)
607 if (PR_FALSE
== pMutex
->isMultiProcess
) {
608 return single_process_sslMutex_Destroy(pMutex
);
610 PORT_Assert(!("sslMutex_Destroy not implemented for multi-process applications !"));
611 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
616 sslMutex_Unlock(sslMutex
*pMutex
)
619 if (PR_FALSE
== pMutex
->isMultiProcess
) {
620 return single_process_sslMutex_Unlock(pMutex
);
622 PORT_Assert(!("sslMutex_Unlock not implemented for multi-process applications !"));
623 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
628 sslMutex_Lock(sslMutex
*pMutex
)
631 if (PR_FALSE
== pMutex
->isMultiProcess
) {
632 return single_process_sslMutex_Lock(pMutex
);
634 PORT_Assert(!("sslMutex_Lock not implemented for multi-process applications !"));
635 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);