- finalize persistent handle support (probably)
[m6w6/ext-http] / http_persistent_handle_api.c
index b07caffdda4220966b5dc4564d48292387fd6a20..ec345fb6a8b88ac413acf75ac683a789b8ed73a5 100644 (file)
@@ -15,7 +15,6 @@
 #include "php_http.h"
 #include "php_http_api.h"
 
-#ifdef HTTP_HAVE_PERSISTENT_HANDLES
 #include "php_http_persistent_handle_api.h"
 
 #ifndef HTTP_DEBUG_PHANDLES
@@ -36,66 +35,152 @@ typedef struct _http_persistent_handle_t {
        void *ptr;
 } http_persistent_handle;
 
-typedef HashTable *http_persistent_handle_list;
+typedef struct _http_persistent_handle_list_t {
+       HashTable free;
+       ulong used;
+} http_persistent_handle_list;
 
 typedef struct _http_persistent_handle_provider_t {
        http_persistent_handle_list list; /* "ident" => array(handles) entries */
        http_persistent_handle_ctor ctor;
        http_persistent_handle_dtor dtor;
+       http_persistent_handle_copy copy;
 } http_persistent_handle_provider;
 
-
-static inline STATUS http_persistent_handle_list_find(http_persistent_handle_list parent_list, http_persistent_handle_list **ident_list, int create TSRMLS_DC)
+static inline http_persistent_handle_list *http_persistent_handle_list_init(http_persistent_handle_list *list)
 {
-       http_persistent_handle_list new_list;
+       int free_list;
        
-       if (SUCCESS == zend_hash_quick_find(parent_list, HTTP_G->persistent.handles.ident.s, HTTP_G->persistent.handles.ident.l, HTTP_G->persistent.handles.ident.h, (void *) ident_list)) {
-               return SUCCESS;
+       if ((free_list = !list)) {
+               list = pemalloc(sizeof(http_persistent_handle_list), 1);
        }
        
-       if (create) {
-               if ((new_list = pemalloc(sizeof(HashTable), 1))) {
-                       if (SUCCESS == zend_hash_init(new_list, 0, NULL, NULL, 1)) {
-                               if (SUCCESS == zend_hash_quick_add(parent_list, 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 *) ident_list)) {
-                                       return SUCCESS;
-                               }
-                               zend_hash_destroy(new_list);
-                       }
-                       pefree(new_list, 1);
+       list->used = 0;
+       
+       if (SUCCESS != zend_hash_init(&list->free, 0, NULL, NULL, 1)) {
+               if (free_list) {
+                       pefree(list, 1);
                }
+               list = NULL;
        }
        
-       return FAILURE;
+       return list;
 }
 
-static inline void http_persistent_handle_list_dtor(http_persistent_handle_list list, http_persistent_handle_dtor dtor)
+static inline void http_persistent_handle_list_dtor(http_persistent_handle_list *list, http_persistent_handle_dtor dtor)
 {
        HashPosition pos;
        http_persistent_handle *handle;
        
-       FOREACH_HASH_VAL(pos, list, handle) {
+       FOREACH_HASH_VAL(pos, &list->free, handle) {
 #if HTTP_DEBUG_PHANDLES
                fprintf(stderr, "DESTROY: %p\n", handle->ptr);
 #endif
                
                dtor(handle->ptr);
        }
-       zend_hash_clean(list);
+       zend_hash_destroy(&list->free);
+}
+
+static inline void http_persistent_handle_list_free(http_persistent_handle_list **list, http_persistent_handle_dtor dtor)
+{
+       http_persistent_handle_list_dtor(*list, dtor);
+       pefree(*list, 1);
+       *list = NULL;
+}
+
+static inline http_persistent_handle_list *http_persistent_handle_list_find(http_persistent_handle_provider *provider TSRMLS_DC)
+{
+       http_persistent_handle_list **list, *new_list;
+       
+       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)) {
+               return *list;
+       }
+       
+       if ((new_list = http_persistent_handle_list_init(NULL))) {
+               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)) {
+                       return *list;
+               }
+               http_persistent_handle_list_free(&new_list, provider->dtor);
+       }
+       
+       return NULL;
+}
+
+static inline STATUS http_persistent_handle_do_acquire(http_persistent_handle_provider *provider, void **handle_ptr TSRMLS_DC)
+{
+       ulong index;
+       http_persistent_handle *handle;
+       http_persistent_handle_list *list;
+       
+       if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
+               zend_hash_internal_pointer_end(&list->free);
+               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)) {
+                       *handle_ptr = handle->ptr;
+                       zend_hash_index_del(&list->free, index);
+               } else {
+                       *handle_ptr = provider->ctor();
+               }
+               
+               if (*handle_ptr) {
+                       ++provider->list.used;
+                       ++list->used;
+                       return SUCCESS;
+               }
+       }
+       
+       return FAILURE;
+}
+
+static inline STATUS http_persistent_handle_do_release(http_persistent_handle_provider *provider, void **handle_ptr TSRMLS_DC)
+{
+       http_persistent_handle_list *list;
+       
+       if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
+               if (provider->list.used >= HTTP_G->persistent.handles.limit) {
+                       provider->dtor(*handle_ptr);
+               } else {
+                       http_persistent_handle handle = {*handle_ptr};
+                       
+                       if (SUCCESS != zend_hash_next_index_insert(&list->free, (void *) &handle, sizeof(http_persistent_handle), NULL)) {
+                               return FAILURE;
+                       }
+               }
+               
+               *handle_ptr = NULL;
+               --provider->list.used;
+               --list->used;
+               return SUCCESS;
+       }
+       
+       return FAILURE;
+}
+
+static inline STATUS http_persistent_handle_do_accrete(http_persistent_handle_provider *provider, void *old_handle, void **new_handle TSRMLS_DC)
+{
+       http_persistent_handle_list *list;
+       
+       if (provider->copy && (*new_handle = provider->copy(old_handle))) {
+               if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
+                       ++list->used;
+               }
+               ++provider->list.used;
+               return SUCCESS;
+       }
+       return FAILURE;
 }
 
 static void http_persistent_handles_hash_dtor(void *p)
 {
        http_persistent_handle_provider *provider = (http_persistent_handle_provider *) p;
-       http_persistent_handle_list *list;
+       http_persistent_handle_list **list;
        HashPosition pos;
        
-       FOREACH_HASH_VAL(pos, provider->list, list) {
-               http_persistent_handle_list_dtor(*list, provider->dtor);
-               zend_hash_destroy(*list);
-               pefree(*list, 1);
+       FOREACH_HASH_VAL(pos, &provider->list.free, list) {
+               http_persistent_handle_list_free(list, provider->dtor);
        }
-       zend_hash_destroy(provider->list);
-       pefree(provider->list, 1);
+       
+       zend_hash_destroy(&provider->list.free);
 }
 
 PHP_MINIT_FUNCTION(http_persistent_handle)
@@ -116,26 +201,25 @@ PHP_MSHUTDOWN_FUNCTION(http_persistent_handle)
        return SUCCESS;
 }
 
-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)
+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)
 {
        STATUS status = FAILURE;
        http_persistent_handle_provider provider;
        
        LOCK();
-       provider.list = pemalloc(sizeof(HashTable), 1);
-       if (provider.list) {
+       if (http_persistent_handle_list_init(&provider.list)) {
                provider.ctor = ctor;
                provider.dtor = dtor;
-               zend_hash_init(provider.list, 0, NULL, NULL, 1);
+               provider.copy = copy;
                
 #if HTTP_DEBUG_PHANDLES
-               fprintf(stderr, "PROVIDE: %p (%s)\n", provider.list, name_str);
+               fprintf(stderr, "PROVIDE: %s\n", name_str);
 #endif
                
                if (SUCCESS == zend_hash_add(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider, sizeof(http_persistent_handle_provider), NULL)) {
                        status = SUCCESS;
                } else {
-                       pefree(provider.list, 1);
+                       http_persistent_handle_list_dtor(&provider.list, dtor);
                }
        }
        UNLOCK();
@@ -146,24 +230,12 @@ PHP_HTTP_API STATUS _http_persistent_handle_provide_ex(const char *name_str, siz
 PHP_HTTP_API STATUS _http_persistent_handle_acquire_ex(const char *name_str, size_t name_len, void **handle_ptr TSRMLS_DC)
 {
        STATUS status = FAILURE;
-       ulong index;
        http_persistent_handle_provider *provider;
-       http_persistent_handle_list *list;
-       http_persistent_handle *handle;
        
        *handle_ptr = NULL;
        LOCK();
        if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider)) {
-               if (SUCCESS == http_persistent_handle_list_find(provider->list, &list, 0 TSRMLS_CC)) {
-                       zend_hash_internal_pointer_end(*list);
-                       if (HASH_KEY_NON_EXISTANT != zend_hash_get_current_key(*list, NULL, &index, 0) && SUCCESS == zend_hash_get_current_data(*list, (void *) &handle)) {
-                               *handle_ptr = handle->ptr;
-                               zend_hash_index_del(*list, index);
-                       }
-               }
-               if (*handle_ptr || (*handle_ptr = provider->ctor())) {
-                       status = SUCCESS;
-               }
+               status = http_persistent_handle_do_acquire(provider, handle_ptr TSRMLS_CC);
        }
        UNLOCK();
        
@@ -178,15 +250,10 @@ PHP_HTTP_API STATUS _http_persistent_handle_release_ex(const char *name_str, siz
 {
        STATUS status = FAILURE;
        http_persistent_handle_provider *provider;
-       http_persistent_handle_list *list;
-       http_persistent_handle handle = {*handle_ptr};
        
        LOCK();
        if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider)) {
-               if (    SUCCESS == http_persistent_handle_list_find(provider->list, &list, 1 TSRMLS_CC) &&
-                               SUCCESS == zend_hash_next_index_insert(*list, (void *) &handle, sizeof(http_persistent_handle), NULL)) {
-                       status = SUCCESS;
-               }
+               status = http_persistent_handle_do_release(provider, handle_ptr TSRMLS_CC);
        }
        UNLOCK();
        
@@ -196,35 +263,58 @@ PHP_HTTP_API STATUS _http_persistent_handle_release_ex(const char *name_str, siz
        
        return status;
 }
+
+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)
+{
+       STATUS status = FAILURE;
+       http_persistent_handle_provider *provider;
+       
+       *new_handle = NULL;
+       LOCK();
+       if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider)) {
+               status = http_persistent_handle_do_accrete(provider, old_handle, new_handle TSRMLS_CC);
+       }
+       UNLOCK();
+       
+#if HTTP_DEBUG_PHANDLES
+       fprintf(stderr, "ACCRETE: %p > %p (%s)\n", old_handle, *new_handle, name_str);
+#endif
        
+       return status;
+}
+
 PHP_HTTP_API void _http_persistent_handle_cleanup_ex(const char *name_str, size_t name_len, int current_ident_only TSRMLS_DC)
 {
        http_persistent_handle_provider *provider;
-       http_persistent_handle_list *list;
+       http_persistent_handle_list *list, **listp;
        HashPosition pos1, pos2;
        
        LOCK();
        if (name_str && name_len) {
                if (SUCCESS == zend_hash_find(&http_persistent_handles_hash, (char *) name_str, name_len+1, (void *) &provider)) {
                        if (current_ident_only) {
-                               if (SUCCESS == http_persistent_handle_list_find(provider->list, &list, 0 TSRMLS_CC)) {
-                                       http_persistent_handle_list_dtor(*list, provider->dtor);
+                               if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
+                                       http_persistent_handle_list_dtor(list, provider->dtor);
+                                       http_persistent_handle_list_init(list);
                                }
                        } else {
-                               FOREACH_HASH_VAL(pos1, provider->list, list) {
-                                       http_persistent_handle_list_dtor(*list, provider->dtor);
+                               FOREACH_HASH_VAL(pos1, &provider->list.free, listp) {
+                                       http_persistent_handle_list_dtor(*listp, provider->dtor);
+                                       http_persistent_handle_list_init(*listp);
                                }
                        }
                }
        } else {
                FOREACH_HASH_VAL(pos1, &http_persistent_handles_hash, provider) {
                        if (current_ident_only) {
-                               if (SUCCESS == http_persistent_handle_list_find(provider->list, &list, 0 TSRMLS_CC)) {
-                                       http_persistent_handle_list_dtor(*list, provider->dtor);
+                               if ((list = http_persistent_handle_list_find(provider TSRMLS_CC))) {
+                                       http_persistent_handle_list_dtor(list, provider->dtor);
+                                       http_persistent_handle_list_init(list);
                                }
                        } else {
-                               FOREACH_HASH_VAL(pos2, provider->list, list) {
-                                       http_persistent_handle_list_dtor(*list, provider->dtor);
+                               FOREACH_HASH_VAL(pos2, &provider->list.free, listp) {
+                                       http_persistent_handle_list_dtor(*listp, provider->dtor);
+                                       http_persistent_handle_list_init(*listp);
                                }
                        }
                }
@@ -232,13 +322,13 @@ PHP_HTTP_API void _http_persistent_handle_cleanup_ex(const char *name_str, size_
        UNLOCK();
 }
 
-PHP_HTTP_API HashTable *_http_persistent_handle_statall_ex(HashTable *ht)
+PHP_HTTP_API HashTable *_http_persistent_handle_statall_ex(HashTable *ht TSRMLS_DC)
 {
-       zval *zlist, *zentry;
+       zval *zentry[2];
        HashPosition pos1, pos2;
        HashKey key1 = initHashKey(0), key2 = initHashKey(0);
        http_persistent_handle_provider *provider;
-       http_persistent_handle_list *list;
+       http_persistent_handle_list **list;
        
        LOCK();
        if (zend_hash_num_elements(&http_persistent_handles_hash)) {
@@ -248,14 +338,20 @@ PHP_HTTP_API HashTable *_http_persistent_handle_statall_ex(HashTable *ht)
                }
                
                FOREACH_HASH_KEYVAL(pos1, &http_persistent_handles_hash, key1, provider) {
-                       MAKE_STD_ZVAL(zlist);
-                       array_init(zlist);
-                       FOREACH_HASH_KEYVAL(pos2, provider->list, key2, list) {
-                               MAKE_STD_ZVAL(zentry);
-                               ZVAL_LONG(zentry, zend_hash_num_elements(*list));
-                               zend_hash_add(Z_ARRVAL_P(zlist), key2.str, key2.len, (void *) &zentry, sizeof(zval *), NULL);
+                       MAKE_STD_ZVAL(zentry[0]);
+                       array_init(zentry[0]);
+                       
+                       FOREACH_HASH_KEYVAL(pos2, &provider->list.free, key2, list) {
+                               MAKE_STD_ZVAL(zentry[1]);
+                               array_init(zentry[1]);
+                               add_assoc_long_ex(zentry[1], ZEND_STRS("used"), (*list)->used);
+                               add_assoc_long_ex(zentry[1], ZEND_STRS("free"), zend_hash_num_elements(&(*list)->free));
+                               
+                               /* use zend_hash_* not add_assoc_* (which is zend_symtable_*) as we want a string even for numbers */
+                               zend_hash_add(Z_ARRVAL_P(zentry[0]), key2.str, key2.len, &zentry[1], sizeof(zval *), NULL);
                        }
-                       zend_hash_add(ht, key1.str, key1.len, (void *) &zlist, sizeof(zval *), NULL);
+                       
+                       zend_hash_add(ht, key1.str, key1.len, &zentry[0], sizeof(zval *), NULL);
                }
        } else if (ht) {
                ht = NULL;
@@ -265,7 +361,6 @@ PHP_HTTP_API HashTable *_http_persistent_handle_statall_ex(HashTable *ht)
        return ht;
 }
 
-#endif /* HTTP_HAVE_PERSISTENT_HANDLES */
 
 /*
  * Local variables: