fixes for windows and 5.3 compatibility
[m6w6/ext-http] / php_http_persistent_handle.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-2011, Michael Wallner <mike@php.net> |
10 +--------------------------------------------------------------------+
11 */
12
13 #include "php_http_api.h"
14
15 #ifndef PHP_HTTP_DEBUG_PHANDLES
16 # define PHP_HTTP_DEBUG_PHANDLES 0
17 #endif
18 #if PHP_HTTP_DEBUG_PHANDLES
19 # undef inline
20 # define inline
21 #endif
22
23 static HashTable php_http_persistent_handles_hash;
24 #ifdef ZTS
25 # define LOCK() tsrm_mutex_lock(php_http_persistent_handles_lock)
26 # define UNLOCK() tsrm_mutex_unlock(php_http_persistent_handles_lock)
27 static MUTEX_T php_http_persistent_handles_lock;
28 #else
29 # define LOCK()
30 # define UNLOCK()
31 #endif
32
33 typedef struct php_http_persistent_handle_list {
34 HashTable free;
35 ulong used;
36 } php_http_persistent_handle_list_t;
37
38 typedef struct php_http_persistent_handle_provider {
39 php_http_persistent_handle_list_t list; /* "ident" => array(handles) entries */
40 php_http_resource_factory_t rf;
41 } php_http_persistent_handle_provider_t;
42
43 static inline php_http_persistent_handle_list_t *php_http_persistent_handle_list_init(php_http_persistent_handle_list_t *list)
44 {
45 int free_list;
46
47 if ((free_list = !list)) {
48 list = pemalloc(sizeof(php_http_persistent_handle_list_t), 1);
49 }
50
51 list->used = 0;
52
53 if (SUCCESS != zend_hash_init(&list->free, 0, NULL, NULL, 1)) {
54 if (free_list) {
55 pefree(list, 1);
56 }
57 list = NULL;
58 }
59
60 return list;
61 }
62
63 static int php_http_persistent_handle_apply_cleanup_ex(void *pp, void *arg TSRMLS_DC)
64 {
65 php_http_resource_factory_t *rf = arg;
66 void **handle = pp;
67
68 #if PHP_HTTP_DEBUG_PHANDLES
69 fprintf(stderr, "DESTROY: %p\n", *handle);
70 #endif
71 php_http_resource_factory_handle_dtor(rf, *handle TSRMLS_CC);
72 return ZEND_HASH_APPLY_REMOVE;
73 }
74
75 static int php_http_persistent_handle_apply_cleanup(void *pp, void *arg TSRMLS_DC)
76 {
77 php_http_resource_factory_t *rf = arg;
78 php_http_persistent_handle_list_t **listp = pp;
79
80 zend_hash_apply_with_argument(&(*listp)->free, php_http_persistent_handle_apply_cleanup_ex, rf TSRMLS_CC);
81 return (*listp)->used ? ZEND_HASH_APPLY_KEEP : ZEND_HASH_APPLY_REMOVE;
82 }
83
84 static inline void php_http_persistent_handle_list_dtor(php_http_persistent_handle_list_t *list, php_http_persistent_handle_provider_t *provider TSRMLS_DC)
85 {
86 #if PHP_HTTP_DEBUG_PHANDLES
87 fprintf(stderr, "LSTDTOR: %p\n", list);
88 #endif
89 zend_hash_apply_with_argument(&list->free, php_http_persistent_handle_apply_cleanup_ex, &provider->rf TSRMLS_CC);
90 zend_hash_destroy(&list->free);
91 }
92
93 static inline void php_http_persistent_handle_list_free(php_http_persistent_handle_list_t **list, php_http_persistent_handle_provider_t *provider TSRMLS_DC)
94 {
95 php_http_persistent_handle_list_dtor(*list, provider TSRMLS_CC);
96 #if PHP_HTTP_DEBUG_PHANDLES
97 fprintf(stderr, "LSTFREE: %p\n", *list);
98 #endif
99 pefree(*list, 1);
100 *list = NULL;
101 }
102
103 static int php_http_persistent_handle_list_apply_dtor(void *listp, void *provider TSRMLS_DC)
104 {
105 php_http_persistent_handle_list_free(listp, provider TSRMLS_CC);
106 return ZEND_HASH_APPLY_REMOVE;
107 }
108
109 static inline php_http_persistent_handle_list_t *php_http_persistent_handle_list_find(php_http_persistent_handle_provider_t *provider, const char *ident_str, size_t ident_len TSRMLS_DC)
110 {
111 php_http_persistent_handle_list_t **list, *new_list;
112
113 if (SUCCESS == zend_symtable_find(&provider->list.free, ident_str, ident_len + 1, (void *) &list)) {
114 #if PHP_HTTP_DEBUG_PHANDLES
115 fprintf(stderr, "LSTFIND: %p\n", *list);
116 #endif
117 return *list;
118 }
119
120 if ((new_list = php_http_persistent_handle_list_init(NULL))) {
121 if (SUCCESS == zend_symtable_update(&provider->list.free, ident_str, ident_len + 1, (void *) &new_list, sizeof(php_http_persistent_handle_list_t *), (void *) &list)) {
122 #if PHP_HTTP_DEBUG_PHANDLES
123 fprintf(stderr, "LSTFIND: %p (new)\n", *list);
124 #endif
125 return *list;
126 }
127 php_http_persistent_handle_list_free(&new_list, provider TSRMLS_CC);
128 }
129
130 return NULL;
131 }
132
133 static inline STATUS php_http_persistent_handle_do_acquire(php_http_persistent_handle_provider_t *provider, const char *ident_str, size_t ident_len, void **handle TSRMLS_DC)
134 {
135 ulong index;
136 void **handle_ptr;
137 php_http_persistent_handle_list_t *list;
138
139 if ((list = php_http_persistent_handle_list_find(provider, ident_str, ident_len TSRMLS_CC))) {
140 zend_hash_internal_pointer_end(&list->free);
141 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_ptr)) {
142 *handle = *handle_ptr;
143 zend_hash_index_del(&list->free, index);
144 } else {
145 *handle = php_http_resource_factory_handle_ctor(&provider->rf TSRMLS_CC);
146 }
147
148 if (*handle) {
149 ++provider->list.used;
150 ++list->used;
151 return SUCCESS;
152 }
153 } else {
154 *handle = NULL;
155 }
156
157 return FAILURE;
158 }
159
160 static inline STATUS php_http_persistent_handle_do_release(php_http_persistent_handle_provider_t *provider, const char *ident_str, size_t ident_len, void **handle TSRMLS_DC)
161 {
162 php_http_persistent_handle_list_t *list;
163
164 if ((list = php_http_persistent_handle_list_find(provider, ident_str, ident_len TSRMLS_CC))) {
165 if (provider->list.used >= PHP_HTTP_G->persistent_handle.limit) {
166 php_http_resource_factory_handle_dtor(&provider->rf, *handle TSRMLS_CC);
167 } else {
168 if (SUCCESS != zend_hash_next_index_insert(&list->free, (void *) handle, sizeof(void *), NULL)) {
169 return FAILURE;
170 }
171 }
172
173 *handle = NULL;
174 --provider->list.used;
175 --list->used;
176 return SUCCESS;
177 }
178
179 return FAILURE;
180 }
181
182 static inline STATUS php_http_persistent_handle_do_accrete(php_http_persistent_handle_provider_t *provider, const char *ident_str, size_t ident_len, void *old_handle, void **new_handle TSRMLS_DC)
183 {
184 php_http_persistent_handle_list_t *list;
185
186 if ((*new_handle = php_http_resource_factory_handle_copy(&provider->rf, old_handle TSRMLS_CC))) {
187 if ((list = php_http_persistent_handle_list_find(provider, ident_str, ident_len TSRMLS_CC))) {
188 ++list->used;
189 }
190 ++provider->list.used;
191 return SUCCESS;
192 }
193 return FAILURE;
194 }
195
196 static void php_http_persistent_handles_hash_dtor(void *p)
197 {
198 php_http_persistent_handle_provider_t *provider = (php_http_persistent_handle_provider_t *) p;
199 TSRMLS_FETCH();
200
201 zend_hash_apply_with_argument(&provider->list.free, php_http_persistent_handle_list_apply_dtor, provider TSRMLS_CC);
202 zend_hash_destroy(&provider->list.free);
203 php_http_resource_factory_dtor(&provider->rf);
204 }
205
206 PHP_MINIT_FUNCTION(http_persistent_handle)
207 {
208 zend_hash_init(&php_http_persistent_handles_hash, 0, NULL, php_http_persistent_handles_hash_dtor, 1);
209 #ifdef ZTS
210 php_http_persistent_handles_lock = tsrm_mutex_alloc();
211 #endif
212 return SUCCESS;
213 }
214
215 PHP_MSHUTDOWN_FUNCTION(http_persistent_handle)
216 {
217 zend_hash_destroy(&php_http_persistent_handles_hash);
218 #ifdef ZTS
219 tsrm_mutex_free(php_http_persistent_handles_lock);
220 #endif
221 return SUCCESS;
222 }
223
224 PHP_HTTP_API STATUS php_http_persistent_handle_provide(const char *name_str, size_t name_len, php_http_resource_factory_ops_t *fops, void *data, void (*dtor)(void *))
225 {
226 STATUS status = FAILURE;
227 php_http_persistent_handle_provider_t provider;
228
229 LOCK();
230 if (php_http_persistent_handle_list_init(&provider.list)) {
231 if (php_http_resource_factory_init(&provider.rf, fops, data, dtor)) {
232 #if PHP_HTTP_DEBUG_PHANDLES
233 fprintf(stderr, "PROVIDE: %s\n", name_str);
234 #endif
235
236 if (SUCCESS == zend_symtable_update(&php_http_persistent_handles_hash, name_str, name_len+1, (void *) &provider, sizeof(php_http_persistent_handle_provider_t), NULL)) {
237 status = SUCCESS;
238 } else {
239 php_http_resource_factory_dtor(&provider.rf);
240 }
241 }
242 }
243 UNLOCK();
244
245 return status;
246 }
247
248 PHP_HTTP_API php_http_persistent_handle_factory_t *php_http_persistent_handle_concede(php_http_persistent_handle_factory_t *a, const char *name_str, size_t name_len, const char *ident_str, size_t ident_len TSRMLS_DC)
249 {
250 STATUS status = FAILURE;
251 php_http_persistent_handle_factory_t *free_a = NULL;
252
253 if (!a) {
254 free_a = a = emalloc(sizeof(*a));
255 }
256 memset(a, 0, sizeof(*a));
257
258 LOCK();
259 status = zend_symtable_find(&php_http_persistent_handles_hash, name_str, name_len+1, (void *) &a->provider);
260 UNLOCK();
261
262 if (SUCCESS == status) {
263 a->ident.str = estrndup(ident_str, a->ident.len = ident_len);
264 if (free_a) {
265 a->free_on_abandon = 1;
266 }
267 } else {
268 if (free_a) {
269 efree(free_a);
270 }
271 a = NULL;
272 }
273
274 #if PHP_HTTP_DEBUG_PHANDLES
275 fprintf(stderr, "CONCEDE: %p (%s) (%s)\n", a ? a->provider : NULL, name_str, ident_str);
276 #endif
277
278 return a;
279 }
280
281 PHP_HTTP_API void php_http_persistent_handle_abandon(php_http_persistent_handle_factory_t *a)
282 {
283 zend_bool f = a->free_on_abandon;
284
285 STR_FREE(a->ident.str);
286 memset(a, 0, sizeof(*a));
287 if (f) {
288 efree(a);
289 }
290 }
291
292 PHP_HTTP_API void *php_http_persistent_handle_acquire(php_http_persistent_handle_factory_t *a TSRMLS_DC)
293 {
294 void *handle = NULL;
295
296 LOCK();
297 php_http_persistent_handle_do_acquire(a->provider, a->ident.str, a->ident.len, &handle TSRMLS_CC);
298 UNLOCK();
299
300 return handle;
301 }
302
303 PHP_HTTP_API void *php_http_persistent_handle_accrete(php_http_persistent_handle_factory_t *a, void *handle TSRMLS_DC)
304 {
305 void *new_handle = NULL;
306
307 LOCK();
308 php_http_persistent_handle_do_accrete(a->provider, a->ident.str, a->ident.len, handle, &new_handle TSRMLS_CC);
309 UNLOCK();
310
311 return new_handle;
312 }
313
314 PHP_HTTP_API void php_http_persistent_handle_release(php_http_persistent_handle_factory_t *a, void *handle TSRMLS_DC)
315 {
316 LOCK();
317 php_http_persistent_handle_do_release(a->provider, a->ident.str, a->ident.len, &handle TSRMLS_CC);
318 UNLOCK();
319 }
320
321 PHP_HTTP_API void php_http_persistent_handle_cleanup(const char *name_str, size_t name_len, const char *ident_str, size_t ident_len TSRMLS_DC)
322 {
323 php_http_persistent_handle_provider_t *provider;
324 php_http_persistent_handle_list_t *list;
325
326 LOCK();
327 if (name_str && name_len) {
328 if (SUCCESS == zend_symtable_find(&php_http_persistent_handles_hash, name_str, name_len+1, (void *) &provider)) {
329 if (ident_str && ident_len) {
330 if ((list = php_http_persistent_handle_list_find(provider, ident_str, ident_len TSRMLS_CC))) {
331 zend_hash_apply_with_argument(&list->free, php_http_persistent_handle_apply_cleanup_ex, &provider->rf TSRMLS_CC);
332 }
333 } else {
334 zend_hash_apply_with_argument(&provider->list.free, php_http_persistent_handle_apply_cleanup, &provider->list.free TSRMLS_CC);
335 }
336 }
337 } else {
338 HashPosition pos;
339
340 FOREACH_HASH_VAL(pos, &php_http_persistent_handles_hash, provider) {
341 if (ident_str && ident_len) {
342 if ((list = php_http_persistent_handle_list_find(provider, ident_str, ident_len TSRMLS_CC))) {
343 zend_hash_apply_with_argument(&list->free, php_http_persistent_handle_apply_cleanup_ex, &provider->rf TSRMLS_CC);
344 }
345 } else {
346 zend_hash_apply_with_argument(&provider->list.free, php_http_persistent_handle_apply_cleanup, &provider->list.free TSRMLS_CC);
347 }
348 }
349 }
350 UNLOCK();
351 }
352
353 PHP_HTTP_API HashTable *php_http_persistent_handle_statall(HashTable *ht TSRMLS_DC)
354 {
355 zval *zentry[2];
356 HashPosition pos1, pos2;
357 php_http_array_hashkey_t key1 = php_http_array_hashkey_init(0), key2 = php_http_array_hashkey_init(0);
358 php_http_persistent_handle_provider_t *provider;
359 php_http_persistent_handle_list_t **list;
360
361 LOCK();
362 if (zend_hash_num_elements(&php_http_persistent_handles_hash)) {
363 if (!ht) {
364 ALLOC_HASHTABLE(ht);
365 zend_hash_init(ht, 0, NULL, ZVAL_PTR_DTOR, 0);
366 }
367
368 FOREACH_HASH_KEYVAL(pos1, &php_http_persistent_handles_hash, key1, provider) {
369 MAKE_STD_ZVAL(zentry[0]);
370 array_init(zentry[0]);
371
372 FOREACH_HASH_KEYVAL(pos2, &provider->list.free, key2, list) {
373 MAKE_STD_ZVAL(zentry[1]);
374 array_init(zentry[1]);
375 add_assoc_long_ex(zentry[1], ZEND_STRS("used"), (*list)->used);
376 add_assoc_long_ex(zentry[1], ZEND_STRS("free"), zend_hash_num_elements(&(*list)->free));
377 add_assoc_zval_ex(zentry[0], key2.str, key2.len, zentry[1]);
378 }
379
380 zend_symtable_update(ht, key1.str, key1.len, &zentry[0], sizeof(zval *), NULL);
381 }
382 } else if (ht) {
383 ht = NULL;
384 }
385 UNLOCK();
386
387 return ht;
388 }
389
390 static php_http_resource_factory_ops_t php_http_persistent_handle_rf_ops = {
391 (php_http_resource_factory_handle_ctor_t) php_http_persistent_handle_acquire,
392 (php_http_resource_factory_handle_copy_t) php_http_persistent_handle_accrete,
393 (php_http_resource_factory_handle_dtor_t) php_http_persistent_handle_release
394 };
395
396 PHP_HTTP_API php_http_resource_factory_ops_t *php_http_persistent_handle_resource_factory_ops(void)
397 {
398 return &php_http_persistent_handle_rf_ops;
399 }
400
401 /*
402 * Local variables:
403 * tab-width: 4
404 * c-basic-offset: 4
405 * End:
406 * vim600: noet sw=4 ts=4 fdm=marker
407 * vim<600: noet sw=4 ts=4
408 */
409