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: http_persistent_handle_api.c,v 1.9 2007/02/09 14:19:39 mike Exp $ */
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 912 : {
52 : int free_list;
53 :
54 912 : if ((free_list = !list)) {
55 249 : list = pemalloc(sizeof(http_persistent_handle_list), 1);
56 : }
57 :
58 912 : list->used = 0;
59 :
60 912 : if (SUCCESS != zend_hash_init(&list->free, 0, NULL, NULL, 1)) {
61 0 : if (free_list) {
62 0 : pefree(list, 1);
63 : }
64 0 : list = NULL;
65 : }
66 :
67 912 : return list;
68 : }
69 :
70 : static inline void http_persistent_handle_list_dtor(http_persistent_handle_list *list, http_persistent_handle_dtor dtor)
71 251 : {
72 : HashPosition pos;
73 : http_persistent_handle *handle;
74 :
75 525 : FOREACH_HASH_VAL(pos, &list->free, handle) {
76 : #if HTTP_DEBUG_PHANDLES
77 : fprintf(stderr, "DESTROY: %p\n", handle->ptr);
78 : #endif
79 :
80 274 : dtor(handle->ptr);
81 : }
82 251 : zend_hash_destroy(&list->free);
83 251 : }
84 :
85 : static inline void http_persistent_handle_list_free(http_persistent_handle_list **list, http_persistent_handle_dtor dtor)
86 248 : {
87 248 : http_persistent_handle_list_dtor(*list, dtor);
88 248 : pefree(*list, 1);
89 248 : *list = NULL;
90 248 : }
91 :
92 : static inline http_persistent_handle_list *http_persistent_handle_list_find(http_persistent_handle_provider *provider TSRMLS_DC)
93 624 : {
94 : http_persistent_handle_list **list, *new_list;
95 :
96 624 : 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 375 : return *list;
98 : }
99 :
100 249 : if ((new_list = http_persistent_handle_list_init(NULL))) {
101 249 : 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 249 : return *list;
103 : }
104 0 : http_persistent_handle_list_free(&new_list, provider->dtor);
105 : }
106 :
107 0 : return NULL;
108 : }
109 :
110 : static inline STATUS http_persistent_handle_do_acquire(http_persistent_handle_provider *provider, void **handle_ptr TSRMLS_DC)
111 311 : {
112 : ulong index;
113 : http_persistent_handle *handle;
114 : http_persistent_handle_list *list;
115 :
116 311 : if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
117 311 : zend_hash_internal_pointer_end(&list->free);
118 347 : 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 36 : *handle_ptr = handle->ptr;
120 36 : zend_hash_index_del(&list->free, index);
121 : } else {
122 275 : *handle_ptr = provider->ctor();
123 : }
124 :
125 311 : if (*handle_ptr) {
126 311 : ++provider->list.used;
127 311 : ++list->used;
128 311 : return SUCCESS;
129 : }
130 : }
131 :
132 0 : return FAILURE;
133 : }
134 :
135 : static inline STATUS http_persistent_handle_do_release(http_persistent_handle_provider *provider, void **handle_ptr TSRMLS_DC)
136 310 : {
137 : http_persistent_handle_list *list;
138 :
139 310 : if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
140 310 : if (provider->list.used >= HTTP_G->persistent.handles.limit) {
141 0 : provider->dtor(*handle_ptr);
142 : } else {
143 310 : http_persistent_handle handle = {*handle_ptr};
144 :
145 310 : if (SUCCESS != zend_hash_next_index_insert(&list->free, (void *) &handle, sizeof(http_persistent_handle), NULL)) {
146 0 : return FAILURE;
147 : }
148 : }
149 :
150 310 : *handle_ptr = NULL;
151 310 : --provider->list.used;
152 310 : --list->used;
153 310 : return SUCCESS;
154 : }
155 :
156 0 : 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 0 : {
161 : http_persistent_handle_list *list;
162 :
163 0 : if (provider->copy && (*new_handle = provider->copy(old_handle))) {
164 0 : if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
165 0 : ++list->used;
166 : }
167 0 : ++provider->list.used;
168 0 : return SUCCESS;
169 : }
170 0 : return FAILURE;
171 : }
172 :
173 : static void http_persistent_handles_hash_dtor(void *p)
174 657 : {
175 657 : http_persistent_handle_provider *provider = (http_persistent_handle_provider *) p;
176 : http_persistent_handle_list **list;
177 : HashPosition pos;
178 :
179 905 : FOREACH_HASH_VAL(pos, &provider->list.free, list) {
180 248 : http_persistent_handle_list_free(list, provider->dtor);
181 : }
182 :
183 657 : zend_hash_destroy(&provider->list.free);
184 657 : }
185 :
186 : PHP_MINIT_FUNCTION(http_persistent_handle)
187 220 : {
188 220 : 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 220 : return SUCCESS;
193 : }
194 :
195 : PHP_MSHUTDOWN_FUNCTION(http_persistent_handle)
196 219 : {
197 219 : zend_hash_destroy(&http_persistent_handles_hash);
198 : #ifdef ZTS
199 : tsrm_mutex_free(http_persistent_handles_lock);
200 : #endif
201 219 : 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 660 : {
206 660 : STATUS status = FAILURE;
207 : http_persistent_handle_provider provider;
208 :
209 : LOCK();
210 660 : if (http_persistent_handle_list_init(&provider.list)) {
211 660 : provider.ctor = ctor;
212 660 : provider.dtor = dtor;
213 660 : provider.copy = copy;
214 :
215 : #if HTTP_DEBUG_PHANDLES
216 : fprintf(stderr, "PROVIDE: %s\n", name_str);
217 : #endif
218 :
219 660 : if (SUCCESS == zend_hash_add(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider, sizeof(http_persistent_handle_provider), NULL)) {
220 660 : status = SUCCESS;
221 : } else {
222 0 : http_persistent_handle_list_dtor(&provider.list, dtor);
223 : }
224 : }
225 : UNLOCK();
226 :
227 660 : 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 311 : {
232 311 : STATUS status = FAILURE;
233 : http_persistent_handle_provider *provider;
234 :
235 311 : *handle_ptr = NULL;
236 : LOCK();
237 311 : if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider)) {
238 311 : 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 311 : 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 310 : {
251 310 : STATUS status = FAILURE;
252 : http_persistent_handle_provider *provider;
253 :
254 : LOCK();
255 310 : if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider)) {
256 310 : 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 310 : 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 0 : {
269 0 : STATUS status = FAILURE;
270 : http_persistent_handle_provider *provider;
271 :
272 0 : *new_handle = NULL;
273 : LOCK();
274 0 : if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider)) {
275 0 : 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 0 : 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 1 : {
288 : http_persistent_handle_provider *provider;
289 : http_persistent_handle_list *list, **listp;
290 : HashPosition pos1, pos2;
291 :
292 : LOCK();
293 1 : if (name_str && name_len) {
294 0 : if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider)) {
295 0 : if (current_ident_only) {
296 0 : if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
297 0 : http_persistent_handle_list_dtor(list, provider->dtor);
298 0 : http_persistent_handle_list_init(list);
299 : }
300 : } else {
301 0 : FOREACH_HASH_VAL(pos1, &provider->list.free, listp) {
302 0 : http_persistent_handle_list_dtor(*listp, provider->dtor);
303 0 : http_persistent_handle_list_init(*listp);
304 : }
305 : }
306 : }
307 : } else {
308 4 : FOREACH_HASH_VAL(pos1, &http_persistent_handles_hash, provider) {
309 3 : if (current_ident_only) {
310 3 : if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
311 3 : http_persistent_handle_list_dtor(list, provider->dtor);
312 3 : http_persistent_handle_list_init(list);
313 : }
314 : } else {
315 0 : FOREACH_HASH_VAL(pos2, &provider->list.free, listp) {
316 0 : http_persistent_handle_list_dtor(*listp, provider->dtor);
317 0 : http_persistent_handle_list_init(*listp);
318 : }
319 : }
320 : }
321 : }
322 : UNLOCK();
323 1 : }
324 :
325 : PHP_HTTP_API HashTable *_http_persistent_handle_statall_ex(HashTable *ht TSRMLS_DC)
326 3 : {
327 : zval *zentry[2];
328 : HashPosition pos1, pos2;
329 3 : HashKey key1 = initHashKey(0), key2 = initHashKey(0);
330 : http_persistent_handle_provider *provider;
331 : http_persistent_handle_list **list;
332 :
333 : LOCK();
334 3 : if (zend_hash_num_elements(&http_persistent_handles_hash)) {
335 3 : if (!ht) {
336 0 : ALLOC_HASHTABLE(ht);
337 0 : zend_hash_init(ht, 0, NULL, ZVAL_PTR_DTOR, 0);
338 : }
339 :
340 12 : FOREACH_HASH_KEYVAL(pos1, &http_persistent_handles_hash, key1, provider) {
341 9 : MAKE_STD_ZVAL(zentry[0]);
342 9 : array_init(zentry[0]);
343 :
344 15 : FOREACH_HASH_KEYVAL(pos2, &provider->list.free, key2, list) {
345 6 : MAKE_STD_ZVAL(zentry[1]);
346 6 : array_init(zentry[1]);
347 6 : add_assoc_long_ex(zentry[1], ZEND_STRS("used"), (*list)->used);
348 6 : 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 6 : zend_hash_add(Z_ARRVAL_P(zentry[0]), key2.str, key2.len, &zentry[1], sizeof(zval *), NULL);
352 : }
353 :
354 9 : zend_hash_add(ht, key1.str, key1.len, &zentry[0], sizeof(zval *), NULL);
355 : }
356 0 : } else if (ht) {
357 0 : ht = NULL;
358 : }
359 : UNLOCK();
360 :
361 3 : 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 :
|