Import from 1.9a8 tarball
[mozilla-nss.git] / security / nss / lib / ssl / sslmutex.c
blobf92205a81b6fad63c29f76d033642b5ee37f7447
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
12 * License.
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.
21 * Contributor(s):
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 $ */
38 #include "seccomon.h"
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)
42 #include "sslmutex.h"
43 #include "prerr.h"
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) {
51 return SECFailure;
53 return SECSuccess;
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);
62 return SECFailure;
64 PR_DestroyLock(pMutex->u.sslLock);
65 return SECSuccess;
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);
74 return SECFailure;
76 PR_Unlock(pMutex->u.sslLock);
77 return SECSuccess;
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);
86 return SECFailure;
88 PR_Lock(pMutex->u.sslLock);
89 return SECSuccess;
92 #if defined(LINUX) || defined(AIX) || defined(VMS) || defined(BEOS) || defined(BSDI) || defined(NETBSD) || defined(OPENBSD)
94 #include <unistd.h>
95 #include <fcntl.h>
96 #include <string.h>
97 #include <errno.h>
98 #include "unix_err.h"
99 #include "pratom.h"
101 #define SSL_MUTEX_MAGIC 0xfeedfd
102 #define NONBLOCKING_POSTS 1 /* maybe this is faster */
104 #if NONBLOCKING_POSTS
106 #ifndef FNONBLOCK
107 #define FNONBLOCK O_NONBLOCK
108 #endif
110 static int
111 setNonBlocking(int fd, int nonBlocking)
113 int flags;
114 int err;
116 flags = fcntl(fd, F_GETFL, 0);
117 if (0 > flags)
118 return flags;
119 if (nonBlocking)
120 flags |= FNONBLOCK;
121 else
122 flags &= ~FNONBLOCK;
123 err = fcntl(fd, F_SETFL, flags);
124 return err;
126 #endif
128 SECStatus
129 sslMutex_Init(sslMutex *pMutex, int shared)
131 int err;
132 PR_ASSERT(pMutex);
133 pMutex->isMultiProcess = (PRBool)(shared != 0);
134 if (!shared) {
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);
143 if (err) {
144 return err;
146 #if NONBLOCKING_POSTS
147 err = setNonBlocking(pMutex->u.pipeStr.mPipes[1], 1);
148 if (err)
149 goto loser;
150 #endif
152 pMutex->u.pipeStr.mPipes[2] = SSL_MUTEX_MAGIC;
154 #if defined(LINUX) && defined(i386)
155 /* Pipe starts out empty */
156 return SECSuccess;
157 #else
158 /* Pipe starts with one byte. */
159 return sslMutex_Unlock(pMutex);
160 #endif
162 loser:
163 nss_MD_unix_map_default_error(errno);
164 close(pMutex->u.pipeStr.mPipes[0]);
165 close(pMutex->u.pipeStr.mPipes[1]);
166 return SECFailure;
169 SECStatus
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);
177 return SECFailure;
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;
187 return SECSuccess;
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.
200 SECStatus
201 sslMutex_Unlock(sslMutex *pMutex)
203 PRInt32 newValue;
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);
210 return SECFailure;
212 /* Do Memory Barrier here. */
213 newValue = PR_AtomicDecrement(&pMutex->u.pipeStr.nWaiters);
214 if (newValue > 0) {
215 int cc;
216 char c = 1;
217 do {
218 cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1);
219 } while (cc < 0 && (errno == EINTR || errno == EAGAIN));
220 if (cc != 1) {
221 if (cc < 0)
222 nss_MD_unix_map_default_error(errno);
223 else
224 PORT_SetError(PR_UNKNOWN_ERROR);
225 return SECFailure;
228 return SECSuccess;
231 SECStatus
232 sslMutex_Lock(sslMutex *pMutex)
234 PRInt32 newValue;
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);
241 return SECFailure;
243 newValue = PR_AtomicIncrement(&pMutex->u.pipeStr.nWaiters);
244 /* Do Memory Barrier here. */
245 if (newValue > 1) {
246 int cc;
247 char c;
248 do {
249 cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1);
250 } while (cc < 0 && errno == EINTR);
251 if (cc != 1) {
252 if (cc < 0)
253 nss_MD_unix_map_default_error(errno);
254 else
255 PORT_SetError(PR_UNKNOWN_ERROR);
256 return SECFailure;
259 return SECSuccess;
262 #else
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.
270 SECStatus
271 sslMutex_Unlock(sslMutex *pMutex)
273 int cc;
274 char c = 1;
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);
282 return SECFailure;
284 do {
285 cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1);
286 } while (cc < 0 && (errno == EINTR || errno == EAGAIN));
287 if (cc != 1) {
288 if (cc < 0)
289 nss_MD_unix_map_default_error(errno);
290 else
291 PORT_SetError(PR_UNKNOWN_ERROR);
292 return SECFailure;
295 return SECSuccess;
298 SECStatus
299 sslMutex_Lock(sslMutex *pMutex)
301 int cc;
302 char c;
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);
310 return SECFailure;
313 do {
314 cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1);
315 } while (cc < 0 && errno == EINTR);
316 if (cc != 1) {
317 if (cc < 0)
318 nss_MD_unix_map_default_error(errno);
319 else
320 PORT_SetError(PR_UNKNOWN_ERROR);
321 return SECFailure;
324 return SECSuccess;
327 #endif
329 #elif defined(WIN32)
331 #include "win32err.h"
333 /* on Windows, we need to find the optimal type of locking mechanism to use
334 for the sslMutex.
336 There are 3 cases :
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
343 #ifdef WINNT
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
352 PR_ASSERT(sem);
353 if (sem) {
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);
366 #endif
368 SECStatus
369 sslMutex_Init(sslMutex *pMutex, int shared)
371 #ifdef WINNT
372 SECStatus retvalue;
373 #endif
374 HANDLE hMutex;
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);
387 #ifdef WINNT
388 /* we need a lock on WINNT for fibers in the parent process */
389 retvalue = sslMutex_2LevelInit(pMutex);
390 if (SECSuccess != retvalue)
391 return SECFailure;
392 #endif
394 if (!pMutex || ((hMutex = pMutex->u.sslMutx) != 0 &&
395 hMutex != INVALID_HANDLE_VALUE)) {
396 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
397 return SECFailure;
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());
404 return SECFailure;
406 pMutex->u.sslMutx = hMutex;
407 return SECSuccess;
410 SECStatus
411 sslMutex_Destroy(sslMutex *pMutex)
413 HANDLE hMutex;
414 int rv;
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 */
423 #ifdef WINNT
424 /* on NT, get rid of the PRLock used for fibers within a process */
425 retvalue = sslMutex_2LevelDestroy(pMutex);
426 #endif
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);
433 return SECFailure;
436 rv = CloseHandle(hMutex); /* ignore error */
437 if (rv) {
438 pMutex->u.sslMutx = hMutex = INVALID_HANDLE_VALUE;
439 } else {
440 nss_MD_win32_map_default_error(GetLastError());
441 retvalue = SECFailure;
443 return retvalue;
446 int
447 sslMutex_Unlock(sslMutex *pMutex)
449 BOOL success = FALSE;
450 HANDLE hMutex;
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);
462 return SECFailure;
464 success = ReleaseMutex(hMutex);
465 if (!success) {
466 nss_MD_win32_map_default_error(GetLastError());
467 return SECFailure;
469 #ifdef WINNT
470 return single_process_sslMutex_Unlock(pMutex);
471 /* release PRLock for other fibers in the process */
472 #else
473 return SECSuccess;
474 #endif
477 int
478 sslMutex_Lock(sslMutex *pMutex)
480 HANDLE hMutex;
481 DWORD event;
482 DWORD lastError;
483 SECStatus rv;
484 SECStatus retvalue = SECSuccess;
485 PR_ASSERT(pMutex != 0);
487 if (PR_FALSE == pMutex->isMultiProcess) {
488 return single_process_sslMutex_Lock(pMutex);
490 #ifdef WINNT
491 /* lock first to preserve from other threads/fibers
492 in the same process */
493 retvalue = single_process_sslMutex_Lock(pMutex);
494 #endif
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);
504 switch (event) {
505 case WAIT_OBJECT_0:
506 case WAIT_ABANDONED:
507 rv = SECSuccess;
508 break;
510 case WAIT_TIMEOUT:
511 #if defined(WAIT_IO_COMPLETION)
512 case WAIT_IO_COMPLETION:
513 #endif
514 default: /* should never happen. nothing we can do. */
515 PR_ASSERT(!("WaitForSingleObject returned invalid value."));
516 PORT_SetError(PR_UNKNOWN_ERROR);
517 rv = SECFailure;
518 break;
520 case WAIT_FAILED: /* failure returns this */
521 rv = SECFailure;
522 lastError = GetLastError(); /* for debugging */
523 nss_MD_win32_map_default_error(lastError);
524 break;
527 if (! (SECSuccess == retvalue && SECSuccess == rv)) {
528 return SECFailure;
531 return SECSuccess;
534 #elif defined(XP_UNIX)
536 #include <errno.h>
537 #include "unix_err.h"
539 SECStatus
540 sslMutex_Init(sslMutex *pMutex, int shared)
542 int rv;
543 PR_ASSERT(pMutex);
544 pMutex->isMultiProcess = (PRBool)(shared != 0);
545 if (!shared) {
546 return single_process_sslMutex_Init(pMutex);
548 do {
549 rv = sem_init(&pMutex->u.sem, shared, 1);
550 } while (rv < 0 && errno == EINTR);
551 if (rv < 0) {
552 nss_MD_unix_map_default_error(errno);
553 return SECFailure;
555 return SECSuccess;
558 SECStatus
559 sslMutex_Destroy(sslMutex *pMutex)
561 int rv;
562 if (PR_FALSE == pMutex->isMultiProcess) {
563 return single_process_sslMutex_Destroy(pMutex);
565 do {
566 rv = sem_destroy(&pMutex->u.sem);
567 } while (rv < 0 && errno == EINTR);
568 if (rv < 0) {
569 nss_MD_unix_map_default_error(errno);
570 return SECFailure;
572 return SECSuccess;
575 SECStatus
576 sslMutex_Unlock(sslMutex *pMutex)
578 int rv;
579 if (PR_FALSE == pMutex->isMultiProcess) {
580 return single_process_sslMutex_Unlock(pMutex);
582 do {
583 rv = sem_post(&pMutex->u.sem);
584 } while (rv < 0 && errno == EINTR);
585 if (rv < 0) {
586 nss_MD_unix_map_default_error(errno);
587 return SECFailure;
589 return SECSuccess;
592 SECStatus
593 sslMutex_Lock(sslMutex *pMutex)
595 int rv;
596 if (PR_FALSE == pMutex->isMultiProcess) {
597 return single_process_sslMutex_Lock(pMutex);
599 do {
600 rv = sem_wait(&pMutex->u.sem);
601 } while (rv < 0 && errno == EINTR);
602 if (rv < 0) {
603 nss_MD_unix_map_default_error(errno);
604 return SECFailure;
606 return SECSuccess;
609 #else
611 SECStatus
612 sslMutex_Init(sslMutex *pMutex, int shared)
614 PR_ASSERT(pMutex);
615 pMutex->isMultiProcess = (PRBool)(shared != 0);
616 if (!shared) {
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);
621 return SECFailure;
624 SECStatus
625 sslMutex_Destroy(sslMutex *pMutex)
627 PR_ASSERT(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);
633 return SECFailure;
636 SECStatus
637 sslMutex_Unlock(sslMutex *pMutex)
639 PR_ASSERT(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);
645 return SECFailure;
648 SECStatus
649 sslMutex_Lock(sslMutex *pMutex)
651 PR_ASSERT(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);
657 return SECFailure;
660 #endif
662 #endif