- fix typo which may cause a crash
[m6w6/ext-http] / http_persistent_handle_api.c
1 /*
2 +--------------------------------------------------------------------+
3 | PECL :: http |
4 +--------------------------------------------------------------------+
5 | Redistribution and use in source and binary forms, with or without |
6 | modification, are permitted provided that the conditions mentioned |
7 | in the accompanying LICENSE file are met. |
8 +--------------------------------------------------------------------+
9 | Copyright (c) 2004-2007, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 /* $Id$ */
14
15 #include "php_http.h"
16 #include "php_http_api.h"
17
18 #include "php_http_persistent_handle_api.h"
19
20 #ifndef HTTP_DEBUG_PHANDLES
21 # define HTTP_DEBUG_PHANDLES 0
22 #endif
23 #if HTTP_DEBUG_PHANDLES
24 # undef inline
25 # define inline
26 #endif
27
28 static HashTable http_persistent_handles_hash;
29 #ifdef ZTS
30 # define LOCK() tsrm_mutex_lock(http_persistent_handles_lock)
31 # define UNLOCK() tsrm_mutex_unlock(http_persistent_handles_lock)
32 static MUTEX_T http_persistent_handles_lock;
33 #else
34 # define LOCK()
35 # define UNLOCK()
36 #endif
37
38 typedef struct _http_persistent_handle_t {
39 void *ptr;
40 } http_persistent_handle;
41
42 typedef struct _http_persistent_handle_list_t {
43 HashTable free;
44 ulong used;
45 } http_persistent_handle_list;
46
47 typedef struct _http_persistent_handle_provider_t {
48 http_persistent_handle_list list; /* "ident" => array(handles) entries */
49 http_persistent_handle_ctor ctor;
50 http_persistent_handle_dtor dtor;
51 http_persistent_handle_copy copy;
52 } http_persistent_handle_provider;
53
54 static inline http_persistent_handle_list *http_persistent_handle_list_init(http_persistent_handle_list *list)
55 {
56 int free_list;
57
58 if ((free_list = !list)) {
59 list = pemalloc(sizeof(http_persistent_handle_list), 1);
60 }
61
62 list->used = 0;
63
64 if (SUCCESS != zend_hash_init(&list->free, 0, NULL, NULL, 1)) {
65 if (free_list) {
66 pefree(list, 1);
67 }
68 list = NULL;
69 }
70
71 return list;
72 }
73
74 static inline void http_persistent_handle_list_dtor(http_persistent_handle_list *list, http_persistent_handle_dtor dtor)
75 {
76 HashPosition pos;
77 http_persistent_handle *handle;
78
79 FOREACH_HASH_VAL(pos, &list->free, handle) {
80 #if HTTP_DEBUG_PHANDLES
81 fprintf(stderr, "DESTROY: %p\n", handle->ptr);
82 #endif
83
84 dtor(handle->ptr);
85 }
86 zend_hash_destroy(&list->free);
87 }
88
89 static inline void http_persistent_handle_list_free(http_persistent_handle_list **list, http_persistent_handle_dtor dtor)
90 {
91 http_persistent_handle_list_dtor(*list, dtor);
92 pefree(*list, 1);
93 *list = NULL;
94 }
95
96 static inline http_persistent_handle_list *http_persistent_handle_list_find(http_persistent_handle_provider *provider TSRMLS_DC)
97 {
98 http_persistent_handle_list **list, *new_list;
99
100 if (SUCCESS == zend_hash_quick_find(&provider->list.free, HTTP_G->persistent.handles.ident.s, HTTP_G->persistent.handles.ident.l, HTTP_G->persistent.handles.ident.h, (void *) &list)) {
101 return *list;
102 }
103
104 if ((new_list = http_persistent_handle_list_init(NULL))) {
105 if (SUCCESS == zend_hash_quick_add(&provider->list.free, HTTP_G->persistent.handles.ident.s, HTTP_G->persistent.handles.ident.l, HTTP_G->persistent.handles.ident.h, (void *) &new_list, sizeof(http_persistent_handle_list), (void *) &list)) {
106 return *list;
107 }
108 http_persistent_handle_list_free(&new_list, provider->dtor);
109 }
110
111 return NULL;
112 }
113
114 static inline STATUS http_persistent_handle_do_acquire(http_persistent_handle_provider *provider, void **handle_ptr TSRMLS_DC)
115 {
116 ulong index;
117 http_persistent_handle *handle;
118 http_persistent_handle_list *list;
119
120 if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
121 zend_hash_internal_pointer_end(&list->free);
122 if (HASH_KEY_NON_EXISTANT != zend_hash_get_current_key(&list->free, NULL, &index, 0) && SUCCESS == zend_hash_get_current_data(&list->free, (void *) &handle)) {
123 *handle_ptr = handle->ptr;
124 zend_hash_index_del(&list->free, index);
125 } else {
126 *handle_ptr = provider->ctor();
127 }
128
129 if (*handle_ptr) {
130 ++provider->list.used;
131 ++list->used;
132 return SUCCESS;
133 }
134 }
135
136 return FAILURE;
137 }
138
139 static inline STATUS http_persistent_handle_do_release(http_persistent_handle_provider *provider, void **handle_ptr TSRMLS_DC)
140 {
141 http_persistent_handle_list *list;
142
143 if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
144 if (provider->list.used >= HTTP_G->persistent.handles.limit) {
145 provider->dtor(*handle_ptr);
146 } else {
147 http_persistent_handle handle = {*handle_ptr};
148
149 if (SUCCESS != zend_hash_next_index_insert(&list->free, (void *) &handle, sizeof(http_persistent_handle), NULL)) {
150 return FAILURE;
151 }
152 }
153
154 *handle_ptr = NULL;
155 --provider->list.used;
156 --list->used;
157 return SUCCESS;
158 }
159
160 return FAILURE;
161 }
162
163 static inline STATUS http_persistent_handle_do_accrete(http_persistent_handle_provider *provider, void *old_handle, void **new_handle TSRMLS_DC)
164 {
165 http_persistent_handle_list *list;
166
167 if (provider->copy && (*new_handle = provider->copy(old_handle))) {
168 if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
169 ++list->used;
170 }
171 ++provider->list.used;
172 return SUCCESS;
173 }
174 return FAILURE;
175 }
176
177 static void http_persistent_handles_hash_dtor(void *p)
178 {
179 http_persistent_handle_provider *provider = (http_persistent_handle_provider *) p;
180 http_persistent_handle_list **list;
181 HashPosition pos;
182
183 FOREACH_HASH_VAL(pos, &provider->list.free, list) {
184 http_persistent_handle_list_free(list, provider->dtor);
185 }
186
187 zend_hash_destroy(&provider->list.free);
188 }
189
190 PHP_MINIT_FUNCTION(http_persistent_handle)
191 {
192 zend_hash_init(&http_persistent_handles_hash, 0, NULL, http_persistent_handles_hash_dtor, 1);
193 #ifdef ZTS
194 http_persistent_handles_lock = tsrm_mutex_alloc();
195 #endif
196 return SUCCESS;
197 }
198
199 PHP_MSHUTDOWN_FUNCTION(http_persistent_handle)
200 {
201 zend_hash_destroy(&http_persistent_handles_hash);
202 #ifdef ZTS
203 tsrm_mutex_free(http_persistent_handles_lock);
204 #endif
205 return SUCCESS;
206 }
207
208 PHP_HTTP_API STATUS _http_persistent_handle_provide_ex(const char *name_str, size_t name_len, http_persistent_handle_ctor ctor, http_persistent_handle_dtor dtor, http_persistent_handle_copy copy)
209 {
210 STATUS status = FAILURE;
211 http_persistent_handle_provider provider;
212
213 LOCK();
214 if (http_persistent_handle_list_init(&provider.list)) {
215 provider.ctor = ctor;
216 provider.dtor = dtor;
217 provider.copy = copy;
218
219 #if HTTP_DEBUG_PHANDLES
220 fprintf(stderr, "PROVIDE: %s\n", name_str);
221 #endif
222
223 if (SUCCESS == zend_hash_add(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider, sizeof(http_persistent_handle_provider), NULL)) {
224 status = SUCCESS;
225 } else {
226 http_persistent_handle_list_dtor(&provider.list, dtor);
227 }
228 }
229 UNLOCK();
230
231 return status;
232 }
233
234 PHP_HTTP_API STATUS _http_persistent_handle_acquire_ex(const char *name_str, size_t name_len, void **handle_ptr TSRMLS_DC)
235 {
236 STATUS status = FAILURE;
237 http_persistent_handle_provider *provider;
238
239 *handle_ptr = NULL;
240 LOCK();
241 if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider)) {
242 status = http_persistent_handle_do_acquire(provider, handle_ptr TSRMLS_CC);
243 }
244 UNLOCK();
245
246 #if HTTP_DEBUG_PHANDLES
247 fprintf(stderr, "ACQUIRE: %p (%s)\n", *handle_ptr, name_str);
248 #endif
249
250 return status;
251 }
252
253 PHP_HTTP_API STATUS _http_persistent_handle_release_ex(const char *name_str, size_t name_len, void **handle_ptr TSRMLS_DC)
254 {
255 STATUS status = FAILURE;
256 http_persistent_handle_provider *provider;
257 #if HTTP_DEBUG_PHANDLES
258 void *handle_tmp = *handle_ptr;
259 #endif
260
261 LOCK();
262 if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider)) {
263 status = http_persistent_handle_do_release(provider, handle_ptr TSRMLS_CC);
264 }
265 UNLOCK();
266
267 #if HTTP_DEBUG_PHANDLES
268 fprintf(stderr, "RELEASE: %p (%s)\n", handle_tmp, name_str);
269 #endif
270
271 return status;
272 }
273
274 PHP_HTTP_API STATUS _http_persistent_handle_accrete_ex(const char *name_str, size_t name_len, void *old_handle, void **new_handle TSRMLS_DC)
275 {
276 STATUS status = FAILURE;
277 http_persistent_handle_provider *provider;
278
279 *new_handle = NULL;
280 LOCK();
281 if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider)) {
282 status = http_persistent_handle_do_accrete(provider, old_handle, new_handle TSRMLS_CC);
283 }
284 UNLOCK();
285
286 #if HTTP_DEBUG_PHANDLES
287 fprintf(stderr, "ACCRETE: %p > %p (%s)\n", old_handle, *new_handle, name_str);
288 #endif
289
290 return status;
291 }
292
293 PHP_HTTP_API void _http_persistent_handle_cleanup_ex(const char *name_str, size_t name_len, int current_ident_only TSRMLS_DC)
294 {
295 http_persistent_handle_provider *provider;
296 http_persistent_handle_list *list, **listp;
297 HashPosition pos1, pos2;
298
299 LOCK();
300 if (name_str && name_len) {
301 if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider)) {
302 if (current_ident_only) {
303 if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
304 http_persistent_handle_list_dtor(list, provider->dtor);
305 http_persistent_handle_list_init(list);
306 }
307 } else {
308 FOREACH_HASH_VAL(pos1, &provider->list.free, listp) {
309 http_persistent_handle_list_dtor(*listp, provider->dtor);
310 http_persistent_handle_list_init(*listp);
311 }
312 }
313 }
314 } else {
315 FOREACH_HASH_VAL(pos1, &http_persistent_handles_hash, provider) {
316 if (current_ident_only) {
317 if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
318 http_persistent_handle_list_dtor(list, provider->dtor);
319 http_persistent_handle_list_init(list);
320 }
321 } else {
322 FOREACH_HASH_VAL(pos2, &provider->list.free, listp) {
323 http_persistent_handle_list_dtor(*listp, provider->dtor);
324 http_persistent_handle_list_init(*listp);
325 }
326 }
327 }
328 }
329 UNLOCK();
330 }
331
332 PHP_HTTP_API HashTable *_http_persistent_handle_statall_ex(HashTable *ht TSRMLS_DC)
333 {
334 zval *zentry[2];
335 HashPosition pos1, pos2;
336 HashKey key1 = initHashKey(0), key2 = initHashKey(0);
337 http_persistent_handle_provider *provider;
338 http_persistent_handle_list **list;
339
340 LOCK();
341 if (zend_hash_num_elements(&http_persistent_handles_hash)) {
342 if (!ht) {
343 ALLOC_HASHTABLE(ht);
344 zend_hash_init(ht, 0, NULL, ZVAL_PTR_DTOR, 0);
345 }
346
347 FOREACH_HASH_KEYVAL(pos1, &http_persistent_handles_hash, key1, provider) {
348 MAKE_STD_ZVAL(zentry[0]);
349 array_init(zentry[0]);
350
351 FOREACH_HASH_KEYVAL(pos2, &provider->list.free, key2, list) {
352 MAKE_STD_ZVAL(zentry[1]);
353 array_init(zentry[1]);
354 add_assoc_long_ex(zentry[1], ZEND_STRS("used"), (*list)->used);
355 add_assoc_long_ex(zentry[1], ZEND_STRS("free"), zend_hash_num_elements(&(*list)->free));
356
357 /* use zend_hash_* not add_assoc_* (which is zend_symtable_*) as we want a string even for numbers */
358 zend_hash_add(Z_ARRVAL_P(zentry[0]), key2.str, key2.len, &zentry[1], sizeof(zval *), NULL);
359 }
360
361 zend_hash_add(ht, key1.str, key1.len, &zentry[0], sizeof(zval *), NULL);
362 }
363 } else if (ht) {
364 ht = NULL;
365 }
366 UNLOCK();
367
368 return ht;
369 }
370
371
372 /*
373 * Local variables:
374 * tab-width: 4
375 * c-basic-offset: 4
376 * End:
377 * vim600: noet sw=4 ts=4 fdm=marker
378 * vim<600: noet sw=4 ts=4
379 */
380