1/*
2 * Copyright (c) 2003-2006,2008,2010-2012 Apple Inc. All Rights Reserved.
3 *
4 * @APPLE_LICENSE_HEADER_START@
5 *
6 * This file contains Original Code and/or Modifications of Original Code
7 * as defined in and that are subject to the Apple Public Source License
8 * Version 2.0 (the 'License'). You may not use this file except in
9 * compliance with the License. Please obtain a copy of the License at
10 * http://www.opensource.apple.com/apsl/ and read it before using this
11 * file.
12 *
13 * The Original Code and all software distributed under the License are
14 * distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
15 * EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
16 * INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
18 * Please see the License for the specific language governing rights and
19 * limitations under the License.
20 *
21 * @APPLE_LICENSE_HEADER_END@
22 *
23 * nsprPortX.c - minimal platform dependent NSPR functions to enable
24 *               use of DER libraries
25 */
26
27#ifndef _NSPR_PORT_X_H_
28#define _NSPR_PORT_X_H_
29
30#include "prmem.h"
31#include "prlock.h"
32#include "prerror.h"
33#include "prinit.h"
34#include "prbit.h"
35
36#include <stdlib.h>
37#include <pthread.h>
38#include <string.h>
39
40// MARK: *** Memory ***
41
42NSPR_API(void *) PR_Malloc(PRSize size)
43{
44	return malloc(size ? size : 1);
45}
46NSPR_API(void *) PR_Calloc(PRSize nelem, PRSize elsize)
47{
48	return calloc(nelem, elsize);
49}
50NSPR_API(void *) PR_Realloc(void *ptr, PRSize size)
51{
52	return realloc(ptr, size);
53}
54NSPR_API(void) PR_Free(void *ptr)
55{
56	return free(ptr);
57}
58
59// MARK: *** locks ***
60
61NSPR_API(PRLock*) PR_NewLock(void)
62{
63	pthread_mutex_t *pm = PR_Malloc(sizeof(pthread_mutex_t));
64	if(pm == NULL) {
65		return NULL;
66	}
67	if(pthread_mutex_init(pm, NULL)) {
68		PR_Free(pm);
69		return NULL;
70	}
71	return (PRLock*)pm;
72}
73
74NSPR_API(void) PR_DestroyLock(PRLock *lock)
75{
76	if(lock == NULL) {
77		return;
78	}
79	pthread_mutex_destroy((pthread_mutex_t *)lock);
80	PR_Free(lock);
81}
82
83NSPR_API(void) PR_Lock(PRLock *lock)
84{
85	if(lock == NULL) {
86		return;
87	}
88	pthread_mutex_lock((pthread_mutex_t *)lock);
89}
90
91NSPR_API(PRStatus) PR_Unlock(PRLock *lock)
92{
93	if(lock == NULL) {
94		return PR_FAILURE;
95	}
96	pthread_mutex_unlock((pthread_mutex_t *)lock);
97	return PR_SUCCESS;
98}
99
100// MARK: *** get/set error ***
101
102/*
103 * key for pthread_{set,get}specific and a lock to ensure it gets
104 * created once
105 */
106static pthread_key_t PR_threadKey;
107static int PR_threadKeyInitFlag;		// we have a PR_threadKey
108static int PR_threadKeyErrorFlag;		// unable to create PR_threadKey
109static pthread_mutex_t PR_threadKeyLock = PTHREAD_MUTEX_INITIALIZER;
110
111/*
112 * The thing that gets stored on a per-thread basis. A pointer to
113 * this is associated with key PR_threadKey. Mallocd in
114 * PR_getThreadErrInfo(); freed directly by free() as
115 * PR_threadKey's destructor.
116 */
117typedef struct {
118	PRInt32 		osError;
119	PRErrorCode 	prError;
120} PR_threadErrInfo;
121
122/*
123 * One-time init of PR_threadKey, returns nonzero on error.
124 * Does not attempt to init PR_threadKey if doCreate is false and
125 * a previous call to this routine resulted in error (i.e., this
126 * is the GetXError() following a failed SetError()).
127 */
128static PRInt32 PR_initThreadKey(
129	int doCreate)
130{
131	PRInt32 prtn = 0;
132	if(PR_threadKeyInitFlag) {
133		/* thread safe since we never clear this flag; we're ready to go */
134		return 0;
135	}
136	pthread_mutex_lock(&PR_threadKeyLock);
137	if(PR_threadKeyErrorFlag && !doCreate) {
138		/* no error to get because the last SetXError failed */
139		prtn = PR_IO_ERROR;
140	}
141	else if(!PR_threadKeyInitFlag) {
142		prtn = pthread_key_create(&PR_threadKey, free);
143		if(prtn) {
144			/* out of pthread_key_t's */
145			PR_threadKeyErrorFlag = 1;
146		}
147		else {
148			PR_threadKeyErrorFlag = 0;	// in case of retry */
149			PR_threadKeyInitFlag = 1;	// success
150		}
151	}
152	pthread_mutex_unlock(&PR_threadKeyLock);
153	return prtn;
154}
155
156/*
157 * Get current thread's PR_threadErrInfo. Create one if doCreate is
158 * true and one does not exist.
159 *
160 * -- A nonzero *threadKeyError on return indicates that we can
161 *    not create a pthread_key_t; in this case we return NULL.
162 * -- Note that NULL return with zero threadKeyError and zero
163 *    doCreate indicates "no per-thread error set yet", which is
164 *    not an error.
165 */
166static PR_threadErrInfo *PR_getThreadErrInfo(
167	int doCreate,
168	PRInt32 *threadKeyError)		// RETURNED, an OSStatus
169{
170	*threadKeyError = PR_initThreadKey(doCreate);
171	if(*threadKeyError) {
172		return NULL;
173	}
174	PR_threadErrInfo *errInfo = pthread_getspecific(PR_threadKey);
175	if((errInfo == NULL) && doCreate) {
176		errInfo = (PR_threadErrInfo *)malloc(sizeof(*errInfo));
177		if(errInfo == NULL) {
178			/*
179			 * malloc failure, retriable failure of this routine (not
180			 * a PR_threadKeyErrorFlag style error).
181			 * Note that this is *not* detected in a subsequent
182			 * GetXError() call, but it will allow for somewhat
183			 * graceful recovery in case some memory gets freed
184			 * up.
185			 */
186			*threadKeyError = PR_OUT_OF_MEMORY_ERROR;
187		}
188		else {
189			memset(errInfo, 0, sizeof(*errInfo));
190			pthread_setspecific(PR_threadKey, errInfo);
191		}
192	}
193	return errInfo;
194}
195
196PR_IMPLEMENT(PRErrorCode) PR_GetError(void)
197{
198	PRInt32 prtn;
199	PR_threadErrInfo *errInfo = PR_getThreadErrInfo(0, &prtn);
200	if(errInfo == NULL) {
201		/* no error set or per-thread logic uninitialized */
202		if(prtn) {
203			return PR_INSUFFICIENT_RESOURCES_ERROR;
204		}
205		else {
206			return 0;
207		}
208	}
209	else {
210		return errInfo->prError;
211	}
212}
213
214PR_IMPLEMENT(PRInt32) PR_GetOSError(void)
215{
216	PRInt32 prtn;
217	PR_threadErrInfo *errInfo = PR_getThreadErrInfo(0, &prtn);
218	if(errInfo == NULL) {
219		/* no error set or per-thread logic uninitialized */
220		return prtn;
221	}
222	else {
223		return errInfo->osError;
224	}
225}
226
227PR_IMPLEMENT(void) PR_SetError(PRErrorCode code, PRInt32 osErr)
228{
229	PRInt32 prtn;
230	PR_threadErrInfo *errInfo = PR_getThreadErrInfo(1, &prtn);
231	if(errInfo != NULL) {
232		errInfo->osError = osErr;
233		errInfo->prError = code;
234	}
235	/* else per-thread logic uninitialized */
236}
237
238// MARK: *** misc. ***
239
240/*
241** Compute the log of the least power of 2 greater than or equal to n
242*/
243NSPR_API(PRIntn) PR_CeilingLog2(PRUint32 i)
244{
245	PRIntn r;
246	PR_CEILING_LOG2(r,i);
247	return r;
248}
249
250#endif	/* _NSPR_PORT_X_H_ */
251