1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 2001
19 * the Initial Developer. All Rights Reserved.
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
36 /* $Id: sslmutex.c,v 1.20 2006/06/07 18:36:26 nelson%bolyard.com Exp $ */
39 /* This ifdef should match the one in sslsnce.c */
40 #if (defined(XP_UNIX) || defined(XP_WIN32) || defined (XP_OS2) || defined(XP_BEOS)) && !defined(_WIN32_WCE)
45 static SECStatus
single_process_sslMutex_Init(sslMutex
* pMutex
)
47 PR_ASSERT(pMutex
!= 0 && pMutex
->u
.sslLock
== 0 );
49 pMutex
->u
.sslLock
= PR_NewLock();
50 if (!pMutex
->u
.sslLock
) {
56 static SECStatus
single_process_sslMutex_Destroy(sslMutex
* pMutex
)
58 PR_ASSERT(pMutex
!= 0);
59 PR_ASSERT(pMutex
->u
.sslLock
!= 0);
60 if (!pMutex
->u
.sslLock
) {
61 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
64 PR_DestroyLock(pMutex
->u
.sslLock
);
68 static SECStatus
single_process_sslMutex_Unlock(sslMutex
* pMutex
)
70 PR_ASSERT(pMutex
!= 0 );
71 PR_ASSERT(pMutex
->u
.sslLock
!=0);
72 if (!pMutex
->u
.sslLock
) {
73 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
76 PR_Unlock(pMutex
->u
.sslLock
);
80 static SECStatus
single_process_sslMutex_Lock(sslMutex
* pMutex
)
82 PR_ASSERT(pMutex
!= 0);
83 PR_ASSERT(pMutex
->u
.sslLock
!= 0 );
84 if (!pMutex
->u
.sslLock
) {
85 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
88 PR_Lock(pMutex
->u
.sslLock
);
92 #if defined(LINUX) || defined(AIX) || defined(VMS) || defined(BEOS) || defined(BSDI) || defined(NETBSD) || defined(OPENBSD)
101 #define SSL_MUTEX_MAGIC 0xfeedfd
102 #define NONBLOCKING_POSTS 1 /* maybe this is faster */
104 #if NONBLOCKING_POSTS
107 #define FNONBLOCK O_NONBLOCK
111 setNonBlocking(int fd
, int nonBlocking
)
116 flags
= fcntl(fd
, F_GETFL
, 0);
123 err
= fcntl(fd
, F_SETFL
, flags
);
129 sslMutex_Init(sslMutex
*pMutex
, int shared
)
133 pMutex
->isMultiProcess
= (PRBool
)(shared
!= 0);
135 return single_process_sslMutex_Init(pMutex
);
137 pMutex
->u
.pipeStr
.mPipes
[0] = -1;
138 pMutex
->u
.pipeStr
.mPipes
[1] = -1;
139 pMutex
->u
.pipeStr
.mPipes
[2] = -1;
140 pMutex
->u
.pipeStr
.nWaiters
= 0;
142 err
= pipe(pMutex
->u
.pipeStr
.mPipes
);
146 #if NONBLOCKING_POSTS
147 err
= setNonBlocking(pMutex
->u
.pipeStr
.mPipes
[1], 1);
152 pMutex
->u
.pipeStr
.mPipes
[2] = SSL_MUTEX_MAGIC
;
154 #if defined(LINUX) && defined(i386)
155 /* Pipe starts out empty */
158 /* Pipe starts with one byte. */
159 return sslMutex_Unlock(pMutex
);
163 nss_MD_unix_map_default_error(errno
);
164 close(pMutex
->u
.pipeStr
.mPipes
[0]);
165 close(pMutex
->u
.pipeStr
.mPipes
[1]);
170 sslMutex_Destroy(sslMutex
*pMutex
)
172 if (PR_FALSE
== pMutex
->isMultiProcess
) {
173 return single_process_sslMutex_Destroy(pMutex
);
175 if (pMutex
->u
.pipeStr
.mPipes
[2] != SSL_MUTEX_MAGIC
) {
176 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
179 close(pMutex
->u
.pipeStr
.mPipes
[0]);
180 close(pMutex
->u
.pipeStr
.mPipes
[1]);
182 pMutex
->u
.pipeStr
.mPipes
[0] = -1;
183 pMutex
->u
.pipeStr
.mPipes
[1] = -1;
184 pMutex
->u
.pipeStr
.mPipes
[2] = -1;
185 pMutex
->u
.pipeStr
.nWaiters
= 0;
190 #if defined(LINUX) && defined(i386)
191 /* No memory barrier needed for this platform */
193 /* nWaiters includes the holder of the lock (if any) and the number
194 ** threads waiting for it. After incrementing nWaiters, if the count
195 ** is exactly 1, then you have the lock and may proceed. If the
196 ** count is greater than 1, then you must wait on the pipe.
201 sslMutex_Unlock(sslMutex
*pMutex
)
204 if (PR_FALSE
== pMutex
->isMultiProcess
) {
205 return single_process_sslMutex_Unlock(pMutex
);
208 if (pMutex
->u
.pipeStr
.mPipes
[2] != SSL_MUTEX_MAGIC
) {
209 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
212 /* Do Memory Barrier here. */
213 newValue
= PR_AtomicDecrement(&pMutex
->u
.pipeStr
.nWaiters
);
218 cc
= write(pMutex
->u
.pipeStr
.mPipes
[1], &c
, 1);
219 } while (cc
< 0 && (errno
== EINTR
|| errno
== EAGAIN
));
222 nss_MD_unix_map_default_error(errno
);
224 PORT_SetError(PR_UNKNOWN_ERROR
);
232 sslMutex_Lock(sslMutex
*pMutex
)
235 if (PR_FALSE
== pMutex
->isMultiProcess
) {
236 return single_process_sslMutex_Lock(pMutex
);
239 if (pMutex
->u
.pipeStr
.mPipes
[2] != SSL_MUTEX_MAGIC
) {
240 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
243 newValue
= PR_AtomicIncrement(&pMutex
->u
.pipeStr
.nWaiters
);
244 /* Do Memory Barrier here. */
249 cc
= read(pMutex
->u
.pipeStr
.mPipes
[0], &c
, 1);
250 } while (cc
< 0 && errno
== EINTR
);
253 nss_MD_unix_map_default_error(errno
);
255 PORT_SetError(PR_UNKNOWN_ERROR
);
264 /* Using Atomic operations requires the use of a memory barrier instruction
265 ** on PowerPC, Sparc, and Alpha. NSPR's PR_Atomic functions do not perform
266 ** them, and NSPR does not provide a function that does them (e.g. PR_Barrier).
267 ** So, we don't use them on those platforms.
271 sslMutex_Unlock(sslMutex
*pMutex
)
276 if (PR_FALSE
== pMutex
->isMultiProcess
) {
277 return single_process_sslMutex_Unlock(pMutex
);
280 if (pMutex
->u
.pipeStr
.mPipes
[2] != SSL_MUTEX_MAGIC
) {
281 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
285 cc
= write(pMutex
->u
.pipeStr
.mPipes
[1], &c
, 1);
286 } while (cc
< 0 && (errno
== EINTR
|| errno
== EAGAIN
));
289 nss_MD_unix_map_default_error(errno
);
291 PORT_SetError(PR_UNKNOWN_ERROR
);
299 sslMutex_Lock(sslMutex
*pMutex
)
304 if (PR_FALSE
== pMutex
->isMultiProcess
) {
305 return single_process_sslMutex_Lock(pMutex
);
308 if (pMutex
->u
.pipeStr
.mPipes
[2] != SSL_MUTEX_MAGIC
) {
309 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
314 cc
= read(pMutex
->u
.pipeStr
.mPipes
[0], &c
, 1);
315 } while (cc
< 0 && errno
== EINTR
);
318 nss_MD_unix_map_default_error(errno
);
320 PORT_SetError(PR_UNKNOWN_ERROR
);
331 #include "win32err.h"
333 /* on Windows, we need to find the optimal type of locking mechanism to use
337 1) single-process, use a PRLock, as for all other platforms
338 2) Win95 multi-process, use a Win32 mutex
339 3) on WINNT multi-process, use a PRLock + a Win32 mutex
345 SECStatus
sslMutex_2LevelInit(sslMutex
*sem
)
347 /* the following adds a PRLock to sslMutex . This is done in each
348 process of a multi-process server and is only needed on WINNT, if
349 using fibers. We can't tell if native threads or fibers are used, so
350 we always do it on WINNT
354 /* we need to reset the sslLock in the children or the single_process init
355 function below will assert */
356 sem
->u
.sslLock
= NULL
;
358 return single_process_sslMutex_Init(sem
);
361 static SECStatus
sslMutex_2LevelDestroy(sslMutex
*sem
)
363 return single_process_sslMutex_Destroy(sem
);
369 sslMutex_Init(sslMutex
*pMutex
, int shared
)
375 SECURITY_ATTRIBUTES attributes
=
376 { sizeof(SECURITY_ATTRIBUTES
), NULL
, TRUE
};
378 PR_ASSERT(pMutex
!= 0 && (pMutex
->u
.sslMutx
== 0 ||
379 pMutex
->u
.sslMutx
== INVALID_HANDLE_VALUE
) );
381 pMutex
->isMultiProcess
= (PRBool
)(shared
!= 0);
383 if (PR_FALSE
== pMutex
->isMultiProcess
) {
384 return single_process_sslMutex_Init(pMutex
);
388 /* we need a lock on WINNT for fibers in the parent process */
389 retvalue
= sslMutex_2LevelInit(pMutex
);
390 if (SECSuccess
!= retvalue
)
394 if (!pMutex
|| ((hMutex
= pMutex
->u
.sslMutx
) != 0 &&
395 hMutex
!= INVALID_HANDLE_VALUE
)) {
396 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
399 attributes
.bInheritHandle
= (shared
? TRUE
: FALSE
);
400 hMutex
= CreateMutex(&attributes
, FALSE
, NULL
);
401 if (hMutex
== NULL
) {
402 hMutex
= INVALID_HANDLE_VALUE
;
403 nss_MD_win32_map_default_error(GetLastError());
406 pMutex
->u
.sslMutx
= hMutex
;
411 sslMutex_Destroy(sslMutex
*pMutex
)
415 int retvalue
= SECSuccess
;
417 PR_ASSERT(pMutex
!= 0);
418 if (PR_FALSE
== pMutex
->isMultiProcess
) {
419 return single_process_sslMutex_Destroy(pMutex
);
422 /* multi-process mode */
424 /* on NT, get rid of the PRLock used for fibers within a process */
425 retvalue
= sslMutex_2LevelDestroy(pMutex
);
428 PR_ASSERT( pMutex
->u
.sslMutx
!= 0 &&
429 pMutex
->u
.sslMutx
!= INVALID_HANDLE_VALUE
);
430 if (!pMutex
|| (hMutex
= pMutex
->u
.sslMutx
) == 0
431 || hMutex
== INVALID_HANDLE_VALUE
) {
432 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
436 rv
= CloseHandle(hMutex
); /* ignore error */
438 pMutex
->u
.sslMutx
= hMutex
= INVALID_HANDLE_VALUE
;
440 nss_MD_win32_map_default_error(GetLastError());
441 retvalue
= SECFailure
;
447 sslMutex_Unlock(sslMutex
*pMutex
)
449 BOOL success
= FALSE
;
452 PR_ASSERT(pMutex
!= 0 );
453 if (PR_FALSE
== pMutex
->isMultiProcess
) {
454 return single_process_sslMutex_Unlock(pMutex
);
457 PR_ASSERT(pMutex
->u
.sslMutx
!= 0 &&
458 pMutex
->u
.sslMutx
!= INVALID_HANDLE_VALUE
);
459 if (!pMutex
|| (hMutex
= pMutex
->u
.sslMutx
) == 0 ||
460 hMutex
== INVALID_HANDLE_VALUE
) {
461 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
464 success
= ReleaseMutex(hMutex
);
466 nss_MD_win32_map_default_error(GetLastError());
470 return single_process_sslMutex_Unlock(pMutex
);
471 /* release PRLock for other fibers in the process */
478 sslMutex_Lock(sslMutex
*pMutex
)
484 SECStatus retvalue
= SECSuccess
;
485 PR_ASSERT(pMutex
!= 0);
487 if (PR_FALSE
== pMutex
->isMultiProcess
) {
488 return single_process_sslMutex_Lock(pMutex
);
491 /* lock first to preserve from other threads/fibers
492 in the same process */
493 retvalue
= single_process_sslMutex_Lock(pMutex
);
495 PR_ASSERT(pMutex
->u
.sslMutx
!= 0 &&
496 pMutex
->u
.sslMutx
!= INVALID_HANDLE_VALUE
);
497 if (!pMutex
|| (hMutex
= pMutex
->u
.sslMutx
) == 0 ||
498 hMutex
== INVALID_HANDLE_VALUE
) {
499 PORT_SetError(PR_INVALID_ARGUMENT_ERROR
);
500 return SECFailure
; /* what else ? */
502 /* acquire the mutex to be the only owner accross all other processes */
503 event
= WaitForSingleObject(hMutex
, INFINITE
);
511 #if defined(WAIT_IO_COMPLETION)
512 case WAIT_IO_COMPLETION
:
514 default: /* should never happen. nothing we can do. */
515 PR_ASSERT(!("WaitForSingleObject returned invalid value."));
516 PORT_SetError(PR_UNKNOWN_ERROR
);
520 case WAIT_FAILED
: /* failure returns this */
522 lastError
= GetLastError(); /* for debugging */
523 nss_MD_win32_map_default_error(lastError
);
527 if (! (SECSuccess
== retvalue
&& SECSuccess
== rv
)) {
534 #elif defined(XP_UNIX)
537 #include "unix_err.h"
540 sslMutex_Init(sslMutex
*pMutex
, int shared
)
544 pMutex
->isMultiProcess
= (PRBool
)(shared
!= 0);
546 return single_process_sslMutex_Init(pMutex
);
549 rv
= sem_init(&pMutex
->u
.sem
, shared
, 1);
550 } while (rv
< 0 && errno
== EINTR
);
552 nss_MD_unix_map_default_error(errno
);
559 sslMutex_Destroy(sslMutex
*pMutex
)
562 if (PR_FALSE
== pMutex
->isMultiProcess
) {
563 return single_process_sslMutex_Destroy(pMutex
);
566 rv
= sem_destroy(&pMutex
->u
.sem
);
567 } while (rv
< 0 && errno
== EINTR
);
569 nss_MD_unix_map_default_error(errno
);
576 sslMutex_Unlock(sslMutex
*pMutex
)
579 if (PR_FALSE
== pMutex
->isMultiProcess
) {
580 return single_process_sslMutex_Unlock(pMutex
);
583 rv
= sem_post(&pMutex
->u
.sem
);
584 } while (rv
< 0 && errno
== EINTR
);
586 nss_MD_unix_map_default_error(errno
);
593 sslMutex_Lock(sslMutex
*pMutex
)
596 if (PR_FALSE
== pMutex
->isMultiProcess
) {
597 return single_process_sslMutex_Lock(pMutex
);
600 rv
= sem_wait(&pMutex
->u
.sem
);
601 } while (rv
< 0 && errno
== EINTR
);
603 nss_MD_unix_map_default_error(errno
);
612 sslMutex_Init(sslMutex
*pMutex
, int shared
)
615 pMutex
->isMultiProcess
= (PRBool
)(shared
!= 0);
617 return single_process_sslMutex_Init(pMutex
);
619 PORT_Assert(!("sslMutex_Init not implemented for multi-process applications !"));
620 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
625 sslMutex_Destroy(sslMutex
*pMutex
)
628 if (PR_FALSE
== pMutex
->isMultiProcess
) {
629 return single_process_sslMutex_Destroy(pMutex
);
631 PORT_Assert(!("sslMutex_Destroy not implemented for multi-process applications !"));
632 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
637 sslMutex_Unlock(sslMutex
*pMutex
)
640 if (PR_FALSE
== pMutex
->isMultiProcess
) {
641 return single_process_sslMutex_Unlock(pMutex
);
643 PORT_Assert(!("sslMutex_Unlock not implemented for multi-process applications !"));
644 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);
649 sslMutex_Lock(sslMutex
*pMutex
)
652 if (PR_FALSE
== pMutex
->isMultiProcess
) {
653 return single_process_sslMutex_Lock(pMutex
);
655 PORT_Assert(!("sslMutex_Lock not implemented for multi-process applications !"));
656 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR
);