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