Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / net / third_party / nss / ssl / sslmutex.c
blobff6368069df283ff288ca37163b46be0447c8bfb
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/. */
5 #include "seccomon.h"
6 /* This ifdef should match the one in sslsnce.c */
7 #if defined(XP_UNIX) || defined(XP_WIN32) || defined (XP_OS2) || defined(XP_BEOS)
9 #include "sslmutex.h"
10 #include "prerr.h"
12 static SECStatus single_process_sslMutex_Init(sslMutex* pMutex)
14 PR_ASSERT(pMutex != 0 && pMutex->u.sslLock == 0 );
16 pMutex->u.sslLock = PR_NewLock();
17 if (!pMutex->u.sslLock) {
18 return SECFailure;
20 return SECSuccess;
23 static SECStatus single_process_sslMutex_Destroy(sslMutex* pMutex)
25 PR_ASSERT(pMutex != 0);
26 PR_ASSERT(pMutex->u.sslLock!= 0);
27 if (!pMutex->u.sslLock) {
28 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
29 return SECFailure;
31 PR_DestroyLock(pMutex->u.sslLock);
32 return SECSuccess;
35 static SECStatus single_process_sslMutex_Unlock(sslMutex* pMutex)
37 PR_ASSERT(pMutex != 0 );
38 PR_ASSERT(pMutex->u.sslLock !=0);
39 if (!pMutex->u.sslLock) {
40 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
41 return SECFailure;
43 PR_Unlock(pMutex->u.sslLock);
44 return SECSuccess;
47 static SECStatus single_process_sslMutex_Lock(sslMutex* pMutex)
49 PR_ASSERT(pMutex != 0);
50 PR_ASSERT(pMutex->u.sslLock != 0 );
51 if (!pMutex->u.sslLock) {
52 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
53 return SECFailure;
55 PR_Lock(pMutex->u.sslLock);
56 return SECSuccess;
59 #if defined(LINUX) || defined(AIX) || defined(BEOS) || defined(BSDI) || (defined(NETBSD) && __NetBSD_Version__ < 500000000) || defined(OPENBSD)
61 #include <unistd.h>
62 #include <fcntl.h>
63 #include <string.h>
64 #include <errno.h>
65 #include "unix_err.h"
66 #include "pratom.h"
68 #define SSL_MUTEX_MAGIC 0xfeedfd
69 #define NONBLOCKING_POSTS 1 /* maybe this is faster */
71 #if NONBLOCKING_POSTS
73 #ifndef FNONBLOCK
74 #define FNONBLOCK O_NONBLOCK
75 #endif
77 static int
78 setNonBlocking(int fd, int nonBlocking)
80 int flags;
81 int err;
83 flags = fcntl(fd, F_GETFL, 0);
84 if (0 > flags)
85 return flags;
86 if (nonBlocking)
87 flags |= FNONBLOCK;
88 else
89 flags &= ~FNONBLOCK;
90 err = fcntl(fd, F_SETFL, flags);
91 return err;
93 #endif
95 SECStatus
96 sslMutex_Init(sslMutex *pMutex, int shared)
98 int err;
99 PR_ASSERT(pMutex);
100 pMutex->isMultiProcess = (PRBool)(shared != 0);
101 if (!shared) {
102 return single_process_sslMutex_Init(pMutex);
104 pMutex->u.pipeStr.mPipes[0] = -1;
105 pMutex->u.pipeStr.mPipes[1] = -1;
106 pMutex->u.pipeStr.mPipes[2] = -1;
107 pMutex->u.pipeStr.nWaiters = 0;
109 err = pipe(pMutex->u.pipeStr.mPipes);
110 if (err) {
111 nss_MD_unix_map_default_error(errno);
112 return err;
114 #if NONBLOCKING_POSTS
115 err = setNonBlocking(pMutex->u.pipeStr.mPipes[1], 1);
116 if (err)
117 goto loser;
118 #endif
120 pMutex->u.pipeStr.mPipes[2] = SSL_MUTEX_MAGIC;
122 #if defined(LINUX) && defined(i386)
123 /* Pipe starts out empty */
124 return SECSuccess;
125 #else
126 /* Pipe starts with one byte. */
127 return sslMutex_Unlock(pMutex);
128 #endif
130 loser:
131 nss_MD_unix_map_default_error(errno);
132 close(pMutex->u.pipeStr.mPipes[0]);
133 close(pMutex->u.pipeStr.mPipes[1]);
134 return SECFailure;
137 SECStatus
138 sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal)
140 if (PR_FALSE == pMutex->isMultiProcess) {
141 return single_process_sslMutex_Destroy(pMutex);
143 if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
144 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
145 return SECFailure;
147 close(pMutex->u.pipeStr.mPipes[0]);
148 close(pMutex->u.pipeStr.mPipes[1]);
150 if (processLocal) {
151 return SECSuccess;
154 pMutex->u.pipeStr.mPipes[0] = -1;
155 pMutex->u.pipeStr.mPipes[1] = -1;
156 pMutex->u.pipeStr.mPipes[2] = -1;
157 pMutex->u.pipeStr.nWaiters = 0;
159 return SECSuccess;
162 #if defined(LINUX) && defined(i386)
163 /* No memory barrier needed for this platform */
165 /* nWaiters includes the holder of the lock (if any) and the number
166 ** threads waiting for it. After incrementing nWaiters, if the count
167 ** is exactly 1, then you have the lock and may proceed. If the
168 ** count is greater than 1, then you must wait on the pipe.
172 SECStatus
173 sslMutex_Unlock(sslMutex *pMutex)
175 PRInt32 newValue;
176 if (PR_FALSE == pMutex->isMultiProcess) {
177 return single_process_sslMutex_Unlock(pMutex);
180 if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
181 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
182 return SECFailure;
184 /* Do Memory Barrier here. */
185 newValue = PR_ATOMIC_DECREMENT(&pMutex->u.pipeStr.nWaiters);
186 if (newValue > 0) {
187 int cc;
188 char c = 1;
189 do {
190 cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1);
191 } while (cc < 0 && (errno == EINTR || errno == EAGAIN));
192 if (cc != 1) {
193 if (cc < 0)
194 nss_MD_unix_map_default_error(errno);
195 else
196 PORT_SetError(PR_UNKNOWN_ERROR);
197 return SECFailure;
200 return SECSuccess;
203 SECStatus
204 sslMutex_Lock(sslMutex *pMutex)
206 PRInt32 newValue;
207 if (PR_FALSE == pMutex->isMultiProcess) {
208 return single_process_sslMutex_Lock(pMutex);
211 if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
212 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
213 return SECFailure;
215 newValue = PR_ATOMIC_INCREMENT(&pMutex->u.pipeStr.nWaiters);
216 /* Do Memory Barrier here. */
217 if (newValue > 1) {
218 int cc;
219 char c;
220 do {
221 cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1);
222 } while (cc < 0 && errno == EINTR);
223 if (cc != 1) {
224 if (cc < 0)
225 nss_MD_unix_map_default_error(errno);
226 else
227 PORT_SetError(PR_UNKNOWN_ERROR);
228 return SECFailure;
231 return SECSuccess;
234 #else
236 /* Using Atomic operations requires the use of a memory barrier instruction
237 ** on PowerPC, Sparc, and Alpha. NSPR's PR_Atomic functions do not perform
238 ** them, and NSPR does not provide a function that does them (e.g. PR_Barrier).
239 ** So, we don't use them on those platforms.
242 SECStatus
243 sslMutex_Unlock(sslMutex *pMutex)
245 int cc;
246 char c = 1;
248 if (PR_FALSE == pMutex->isMultiProcess) {
249 return single_process_sslMutex_Unlock(pMutex);
252 if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
253 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
254 return SECFailure;
256 do {
257 cc = write(pMutex->u.pipeStr.mPipes[1], &c, 1);
258 } while (cc < 0 && (errno == EINTR || errno == EAGAIN));
259 if (cc != 1) {
260 if (cc < 0)
261 nss_MD_unix_map_default_error(errno);
262 else
263 PORT_SetError(PR_UNKNOWN_ERROR);
264 return SECFailure;
267 return SECSuccess;
270 SECStatus
271 sslMutex_Lock(sslMutex *pMutex)
273 int cc;
274 char c;
276 if (PR_FALSE == pMutex->isMultiProcess) {
277 return single_process_sslMutex_Lock(pMutex);
280 if (pMutex->u.pipeStr.mPipes[2] != SSL_MUTEX_MAGIC) {
281 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
282 return SECFailure;
285 do {
286 cc = read(pMutex->u.pipeStr.mPipes[0], &c, 1);
287 } while (cc < 0 && errno == EINTR);
288 if (cc != 1) {
289 if (cc < 0)
290 nss_MD_unix_map_default_error(errno);
291 else
292 PORT_SetError(PR_UNKNOWN_ERROR);
293 return SECFailure;
296 return SECSuccess;
299 #endif
301 #elif defined(WIN32)
303 #include "win32err.h"
305 /* on Windows, we need to find the optimal type of locking mechanism to use
306 for the sslMutex.
308 There are 3 cases :
309 1) single-process, use a PRLock, as for all other platforms
310 2) Win95 multi-process, use a Win32 mutex
311 3) on WINNT multi-process, use a PRLock + a Win32 mutex
315 #ifdef WINNT
317 SECStatus sslMutex_2LevelInit(sslMutex *sem)
319 /* the following adds a PRLock to sslMutex . This is done in each
320 process of a multi-process server and is only needed on WINNT, if
321 using fibers. We can't tell if native threads or fibers are used, so
322 we always do it on WINNT
324 PR_ASSERT(sem);
325 if (sem) {
326 /* we need to reset the sslLock in the children or the single_process init
327 function below will assert */
328 sem->u.sslLock = NULL;
330 return single_process_sslMutex_Init(sem);
333 static SECStatus sslMutex_2LevelDestroy(sslMutex *sem)
335 return single_process_sslMutex_Destroy(sem);
338 #endif
340 SECStatus
341 sslMutex_Init(sslMutex *pMutex, int shared)
343 #ifdef WINNT
344 SECStatus retvalue;
345 #endif
346 HANDLE hMutex;
347 SECURITY_ATTRIBUTES attributes =
348 { sizeof(SECURITY_ATTRIBUTES), NULL, TRUE };
350 PR_ASSERT(pMutex != 0 && (pMutex->u.sslMutx == 0 ||
351 pMutex->u.sslMutx == INVALID_HANDLE_VALUE) );
353 pMutex->isMultiProcess = (PRBool)(shared != 0);
355 if (PR_FALSE == pMutex->isMultiProcess) {
356 return single_process_sslMutex_Init(pMutex);
359 #ifdef WINNT
360 /* we need a lock on WINNT for fibers in the parent process */
361 retvalue = sslMutex_2LevelInit(pMutex);
362 if (SECSuccess != retvalue)
363 return SECFailure;
364 #endif
366 if (!pMutex || ((hMutex = pMutex->u.sslMutx) != 0 &&
367 hMutex != INVALID_HANDLE_VALUE)) {
368 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
369 return SECFailure;
371 attributes.bInheritHandle = (shared ? TRUE : FALSE);
372 hMutex = CreateMutex(&attributes, FALSE, NULL);
373 if (hMutex == NULL) {
374 hMutex = INVALID_HANDLE_VALUE;
375 nss_MD_win32_map_default_error(GetLastError());
376 return SECFailure;
378 pMutex->u.sslMutx = hMutex;
379 return SECSuccess;
382 SECStatus
383 sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal)
385 HANDLE hMutex;
386 int rv;
387 int retvalue = SECSuccess;
389 PR_ASSERT(pMutex != 0);
390 if (PR_FALSE == pMutex->isMultiProcess) {
391 return single_process_sslMutex_Destroy(pMutex);
394 /* multi-process mode */
395 #ifdef WINNT
396 /* on NT, get rid of the PRLock used for fibers within a process */
397 retvalue = sslMutex_2LevelDestroy(pMutex);
398 #endif
400 PR_ASSERT( pMutex->u.sslMutx != 0 &&
401 pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
402 if (!pMutex || (hMutex = pMutex->u.sslMutx) == 0
403 || hMutex == INVALID_HANDLE_VALUE) {
404 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
405 return SECFailure;
408 rv = CloseHandle(hMutex); /* ignore error */
409 if (!processLocal && rv) {
410 pMutex->u.sslMutx = hMutex = INVALID_HANDLE_VALUE;
412 if (!rv) {
413 nss_MD_win32_map_default_error(GetLastError());
414 retvalue = SECFailure;
416 return retvalue;
419 int
420 sslMutex_Unlock(sslMutex *pMutex)
422 BOOL success = FALSE;
423 HANDLE hMutex;
425 PR_ASSERT(pMutex != 0 );
426 if (PR_FALSE == pMutex->isMultiProcess) {
427 return single_process_sslMutex_Unlock(pMutex);
430 PR_ASSERT(pMutex->u.sslMutx != 0 &&
431 pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
432 if (!pMutex || (hMutex = pMutex->u.sslMutx) == 0 ||
433 hMutex == INVALID_HANDLE_VALUE) {
434 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
435 return SECFailure;
437 success = ReleaseMutex(hMutex);
438 if (!success) {
439 nss_MD_win32_map_default_error(GetLastError());
440 return SECFailure;
442 #ifdef WINNT
443 return single_process_sslMutex_Unlock(pMutex);
444 /* release PRLock for other fibers in the process */
445 #else
446 return SECSuccess;
447 #endif
450 int
451 sslMutex_Lock(sslMutex *pMutex)
453 HANDLE hMutex;
454 DWORD event;
455 DWORD lastError;
456 SECStatus rv;
457 SECStatus retvalue = SECSuccess;
458 PR_ASSERT(pMutex != 0);
460 if (PR_FALSE == pMutex->isMultiProcess) {
461 return single_process_sslMutex_Lock(pMutex);
463 #ifdef WINNT
464 /* lock first to preserve from other threads/fibers
465 in the same process */
466 retvalue = single_process_sslMutex_Lock(pMutex);
467 #endif
468 PR_ASSERT(pMutex->u.sslMutx != 0 &&
469 pMutex->u.sslMutx != INVALID_HANDLE_VALUE);
470 if (!pMutex || (hMutex = pMutex->u.sslMutx) == 0 ||
471 hMutex == INVALID_HANDLE_VALUE) {
472 PORT_SetError(PR_INVALID_ARGUMENT_ERROR);
473 return SECFailure; /* what else ? */
475 /* acquire the mutex to be the only owner accross all other processes */
476 event = WaitForSingleObject(hMutex, INFINITE);
477 switch (event) {
478 case WAIT_OBJECT_0:
479 case WAIT_ABANDONED:
480 rv = SECSuccess;
481 break;
483 case WAIT_TIMEOUT:
484 #if defined(WAIT_IO_COMPLETION)
485 case WAIT_IO_COMPLETION:
486 #endif
487 default: /* should never happen. nothing we can do. */
488 PR_ASSERT(!("WaitForSingleObject returned invalid value."));
489 PORT_SetError(PR_UNKNOWN_ERROR);
490 rv = SECFailure;
491 break;
493 case WAIT_FAILED: /* failure returns this */
494 rv = SECFailure;
495 lastError = GetLastError(); /* for debugging */
496 nss_MD_win32_map_default_error(lastError);
497 break;
500 if (! (SECSuccess == retvalue && SECSuccess == rv)) {
501 return SECFailure;
504 return SECSuccess;
507 #elif defined(XP_UNIX)
509 #include <errno.h>
510 #include "unix_err.h"
512 SECStatus
513 sslMutex_Init(sslMutex *pMutex, int shared)
515 int rv;
516 PR_ASSERT(pMutex);
517 pMutex->isMultiProcess = (PRBool)(shared != 0);
518 if (!shared) {
519 return single_process_sslMutex_Init(pMutex);
521 do {
522 rv = sem_init(&pMutex->u.sem, shared, 1);
523 } while (rv < 0 && errno == EINTR);
524 if (rv < 0) {
525 nss_MD_unix_map_default_error(errno);
526 return SECFailure;
528 return SECSuccess;
531 SECStatus
532 sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal)
534 int rv;
535 if (PR_FALSE == pMutex->isMultiProcess) {
536 return single_process_sslMutex_Destroy(pMutex);
539 /* semaphores are global resources. See SEM_DESTROY(3) man page */
540 if (processLocal) {
541 return SECSuccess;
543 do {
544 rv = sem_destroy(&pMutex->u.sem);
545 } while (rv < 0 && errno == EINTR);
546 if (rv < 0) {
547 nss_MD_unix_map_default_error(errno);
548 return SECFailure;
550 return SECSuccess;
553 SECStatus
554 sslMutex_Unlock(sslMutex *pMutex)
556 int rv;
557 if (PR_FALSE == pMutex->isMultiProcess) {
558 return single_process_sslMutex_Unlock(pMutex);
560 do {
561 rv = sem_post(&pMutex->u.sem);
562 } while (rv < 0 && errno == EINTR);
563 if (rv < 0) {
564 nss_MD_unix_map_default_error(errno);
565 return SECFailure;
567 return SECSuccess;
570 SECStatus
571 sslMutex_Lock(sslMutex *pMutex)
573 int rv;
574 if (PR_FALSE == pMutex->isMultiProcess) {
575 return single_process_sslMutex_Lock(pMutex);
577 do {
578 rv = sem_wait(&pMutex->u.sem);
579 } while (rv < 0 && errno == EINTR);
580 if (rv < 0) {
581 nss_MD_unix_map_default_error(errno);
582 return SECFailure;
584 return SECSuccess;
587 #else
589 SECStatus
590 sslMutex_Init(sslMutex *pMutex, int shared)
592 PR_ASSERT(pMutex);
593 pMutex->isMultiProcess = (PRBool)(shared != 0);
594 if (!shared) {
595 return single_process_sslMutex_Init(pMutex);
597 PORT_Assert(!("sslMutex_Init not implemented for multi-process applications !"));
598 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
599 return SECFailure;
602 SECStatus
603 sslMutex_Destroy(sslMutex *pMutex, PRBool processLocal)
605 PR_ASSERT(pMutex);
606 if (PR_FALSE == pMutex->isMultiProcess) {
607 return single_process_sslMutex_Destroy(pMutex);
609 PORT_Assert(!("sslMutex_Destroy not implemented for multi-process applications !"));
610 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
611 return SECFailure;
614 SECStatus
615 sslMutex_Unlock(sslMutex *pMutex)
617 PR_ASSERT(pMutex);
618 if (PR_FALSE == pMutex->isMultiProcess) {
619 return single_process_sslMutex_Unlock(pMutex);
621 PORT_Assert(!("sslMutex_Unlock not implemented for multi-process applications !"));
622 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
623 return SECFailure;
626 SECStatus
627 sslMutex_Lock(sslMutex *pMutex)
629 PR_ASSERT(pMutex);
630 if (PR_FALSE == pMutex->isMultiProcess) {
631 return single_process_sslMutex_Lock(pMutex);
633 PORT_Assert(!("sslMutex_Lock not implemented for multi-process applications !"));
634 PORT_SetError(PR_NOT_IMPLEMENTED_ERROR);
635 return SECFailure;
638 #endif
640 #endif