/* {{{ */ int pthreads_compare_objects(PTHREADS_COMPARE_PASSTHRU_D) { pthreads_object_t *left = PTHREADS_FETCH_FROM(Z_OBJ_P(op1)); pthreads_object_t *right = PTHREADS_FETCH_FROM(Z_OBJ_P(op2)); /* comparing property tables is not useful or efficient for threaded objects */ /* in addition, it might be useful to know if two variables are infact the same physical threaded object */ if (left->monitor == right->monitor) { return 0; } return 1; } /* }}} */
/* {{{ */ zval * pthreads_read_property (PTHREADS_READ_PROPERTY_PASSTHRU_D) { zend_long *guard = NULL; pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object)); rebuild_object_properties(&threaded->std); if (Z_OBJCE_P(object)->__get && (guard = pthreads_get_guard(&threaded->std, member)) && !((*guard) & IN_GET)) { zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fcc = empty_fcall_info_cache; fci.size = sizeof(zend_fcall_info); fci.retval = rv; fci.object = &threaded->std; zend_fcall_info_argn(&fci, 1, member); fcc.initialized = 1; fcc.function_handler = Z_OBJCE_P(object)->__get; fcc.object = &threaded->std; (*guard) |= IN_GET; zend_call_function(&fci, &fcc); (*guard) &= ~IN_GET; zend_fcall_info_args_clear(&fci, 1); } else { pthreads_store_read(object, member, type, rv); } return rv; }
/* {{{ unset an object property */ void pthreads_unset_property(PTHREADS_UNSET_PROPERTY_PASSTHRU_D) { zval *mstring = NULL; PTHREAD pthreads = PTHREADS_FETCH_FROM(object); if (Z_TYPE_P(member) != IS_STRING) { ALLOC_ZVAL(mstring); *mstring = *member; zval_copy_ctor( mstring ); INIT_PZVAL(mstring); convert_to_string(mstring); member = mstring; #if PHP_VERSION_ID > 50399 key = NULL; #endif } if (Z_TYPE_P(member) == IS_STRING) { if (pthreads_store_delete(pthreads->store, Z_STRVAL_P(member), Z_STRLEN_P(member) TSRMLS_CC)!=SUCCESS){ zend_error( E_WARNING, "pthreads has experienced an internal error while deleting %s::$%s", Z_OBJCE_P(object)->name, Z_STRVAL_P(member) ); } } else zend_error(E_WARNING, "pthreads detected an attempt to use an unsupported kind of key in %s", Z_OBJCE_P(object)->name); if (mstring != NULL) { zval_ptr_dtor(&mstring); } }
/* {{{ check if a thread has a property set, wherever it is available */ int pthreads_has_property(PTHREADS_HAS_PROPERTY_PASSTHRU_D) { int isset = 0; zval *mstring = NULL; PTHREAD pthreads = PTHREADS_FETCH_FROM(object); if (Z_TYPE_P(member) != IS_STRING) { ALLOC_ZVAL(mstring); *mstring = *member; zval_copy_ctor( mstring ); INIT_PZVAL(mstring); convert_to_string(mstring); member = mstring; #if PHP_VERSION_ID > 50399 key = NULL; #endif } if (Z_TYPE_P(member) == IS_STRING) { isset = pthreads_store_isset(pthreads->store, Z_STRVAL_P(member), Z_STRLEN_P(member), has_set_exists TSRMLS_CC); } else zend_error(E_WARNING, "pthreads has detected an attempt to use an unsupported kind of key in %s", Z_OBJCE_P(object)->name); if (mstring != NULL) { zval_ptr_dtor(&mstring); } return isset; }
/* {{{ */ void pthreads_write_property(PTHREADS_WRITE_PROPERTY_PASSTHRU_D) { pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object)); zend_bool nulled = 0; rebuild_object_properties(&threaded->std); switch(Z_TYPE_P(value)){ case IS_UNDEF: case IS_STRING: case IS_LONG: case IS_ARRAY: case IS_OBJECT: case IS_NULL: case IS_DOUBLE: case IS_RESOURCE: case IS_TRUE: case IS_FALSE: { zend_long *guard = NULL; if ((member && Z_TYPE_P(member) != IS_NULL) && Z_OBJCE_P(object)->__set && (guard = pthreads_get_guard(&threaded->std, member)) && !((*guard) & IN_SET)) { zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fcc = empty_fcall_info_cache; zval rv; ZVAL_UNDEF(&rv); fci.size = sizeof(zend_fcall_info); fci.retval = &rv; fci.object = &threaded->std; zend_fcall_info_argn(&fci, 2, member, value); fcc.initialized = 1; fcc.function_handler = Z_OBJCE_P(object)->__set; fcc.object = &threaded->std; (*guard) |= IN_SET; zend_call_function(&fci, &fcc); (*guard) &= ~IN_SET; if (Z_TYPE(rv) != IS_UNDEF) zval_dtor(&rv); zend_fcall_info_args_clear(&fci, 1); } else { pthreads_store_write(object, member, value); } } break; default: { zend_throw_exception_ex( spl_ce_RuntimeException, 0, "pthreads detected an attempt to use unsupported data (%s) for %s::$%s", zend_get_type_by_const(Z_TYPE_P(value)), ZSTR_VAL(Z_OBJCE_P(object)->name), Z_STRVAL_P(member)); } } }
/* {{{ reads properties from storage for debug only */ HashTable* pthreads_read_debug(PTHREADS_READ_DEBUG_PASSTHRU_D) { HashTable *table = emalloc(sizeof(HashTable)); zend_hash_init(table, 8, NULL, ZVAL_PTR_DTOR, 0); *is_temp = 1; pthreads_store_tohash( (PTHREADS_FETCH_FROM(object))->store, table TSRMLS_CC ); return table; } /* }}} */
/* {{{ */ HashTable* pthreads_read_properties(PTHREADS_READ_PROPERTIES_PASSTHRU_D) { pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object)); rebuild_object_properties(&threaded->std); pthreads_store_tohash( object, threaded->std.properties); return threaded->std.properties; } /* }}} */
/* {{{ */ HashTable* pthreads_read_debug(PTHREADS_READ_DEBUG_PASSTHRU_D) { HashTable *table = emalloc(sizeof(HashTable)); pthreads_object_t *threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object)); zend_hash_init(table, 8, NULL, ZVAL_PTR_DTOR, 0); *is_temp = 1; pthreads_store_tohash(object, table); return table; } /* }}} */
/* {{{ unset an object property */ void pthreads_unset_property(PTHREADS_UNSET_PROPERTY_PASSTHRU_D) { PTHREAD pthreads = PTHREADS_FETCH_FROM(object); if (pthreads_store_delete(pthreads->store, Z_STRVAL_P(member), Z_STRLEN_P(member) TSRMLS_CC)!=SUCCESS){ zend_error_noreturn( E_WARNING, "pthreads has experienced an internal error while deleting %s::$%s", Z_OBJCE_P(object)->name, Z_STRVAL_P(member) ); } zend_handlers->unset_property(PTHREADS_UNSET_PROPERTY_PASSTHRU_C); }
/* {{{ check if a thread has a property set, wherever it is available */ int pthreads_has_property(PTHREADS_HAS_PROPERTY_PASSTHRU_D) { int isset = 0; PTHREAD pthreads = PTHREADS_FETCH_FROM(object); if (!(isset = pthreads_store_isset( pthreads->store, Z_STRVAL_P(member), Z_STRLEN_P(member), has_set_exists TSRMLS_CC ))) { isset = zend_handlers->has_property(PTHREADS_HAS_PROPERTY_PASSTHRU_C); } return isset; }
/* {{ reads a property from a thread, wherever it is available */ zval * pthreads_read_property (PTHREADS_READ_PROPERTY_PASSTHRU_D) { zval *value = NULL; PTHREAD pthreads = PTHREADS_FETCH_FROM(object); if (Z_TYPE_P(member)==IS_STRING) { if (pthreads_store_read( pthreads->store, Z_STRVAL_P(member), Z_STRLEN_P(member), &value TSRMLS_CC )!=SUCCESS) { value = zend_handlers->read_property(PTHREADS_READ_PROPERTY_PASSTHRU_C); } else zend_handlers->write_property(PTHREADS_WRITE_PROPERTY_PASSTHRU_C); } else value = zend_handlers->read_property(PTHREADS_READ_PROPERTY_PASSTHRU_C); return value; }
/* {{{ reads properties from storage */ HashTable* pthreads_read_properties(PTHREADS_READ_PROPERTIES_PASSTHRU_D) { PTHREAD pobject = PTHREADS_FETCH_FROM(object); #if PHP_VERSION_ID > 50399 rebuild_object_properties(&pobject->std); #endif pthreads_store_tohash( pobject->store, pobject->std.properties TSRMLS_CC ); return pobject->std.properties; } /* }}} */
/* {{{ */ zend_bool pthreads_globals_object_connect(zend_ulong address, zend_class_entry *ce, zval *object) { zend_bool valid = 0; if (!address) return valid; if (pthreads_globals_lock()) { if (zend_hash_index_exists(&PTHREADS_G(objects), address)) { valid = 1; } pthreads_globals_unlock(); } if (valid) { /* * This can be done outside of a critical section because there are only two possibilities: * We own the object: no possible pathway to fault (read free'd memory) * We don't own the object: possibly pathway to fault whether we use critical section or not: * We use a critical section: we create the connection knowing that address cannot be freed while doing so * however, as soon as we leave the section, and before the conext that called this routine can reference the connection * object the creating context may have free'd the object. * We don't use a critical section: the object may be freed while we are creating the connection, causing a fault. * * As always, it's necessary for the programmer to retain the appropriate references so that this does not fault, creating connections * in a critical section would be unecessarily slow, not to mention recursively lock mutex (which is fine, but not ideal). */ if (PTHREADS_IN_CREATOR(((pthreads_object_t*)address))) { /* we own the object in this context */ ZVAL_OBJ(object, &((pthreads_object_t*)address)->std); Z_ADDREF_P(object); } else { /* we do not own the object, create a connection */ if (!ce) { /* we may not know the class, can't use ce directly from zend_object because it is from another context */ ce = zend_lookup_class( ((pthreads_object_t*)address)->std.ce->name); } object_init_ex(object, ce); pthreads_connect( (pthreads_object_t*) address, PTHREADS_FETCH_FROM(Z_OBJ_P(object))); } } return valid; } /* }}} */
/* {{{ pthreads_cast_object */ int pthreads_cast_object(PTHREADS_CAST_PASSTHRU_D) { switch (type) { case IS_ARRAY: { pthreads_store_tohash( (PTHREADS_FETCH_FROM(from))->store, Z_ARRVAL_P(to) TSRMLS_CC ); return SUCCESS; } break; default: return FAILURE; } return SUCCESS; } /* }}} */
/* {{ reads a property from a thread, wherever it is available */ zval * pthreads_read_property (PTHREADS_READ_PROPERTY_PASSTHRU_D) { zval *value = NULL, *mstring = NULL; PTHREAD pthreads = PTHREADS_FETCH_FROM(object); if (Z_TYPE_P(member) != IS_STRING) { ALLOC_ZVAL(mstring); *mstring = *member; zval_copy_ctor( mstring ); INIT_PZVAL(mstring); zend_try { convert_to_string(mstring); } zend_end_try(); member = mstring; #if PHP_VERSION_ID > 50399 key = NULL; #endif }
/* {{{ pthreads_get_method will attempt to apply pthreads specific modifiers */ zend_function * pthreads_get_method(PTHREADS_GET_METHOD_PASSTHRU_D) { zend_class_entry *scope; zend_function *call; zend_function *callable; char *lcname; int access = 0; PTHREAD thread = PTHREADS_FETCH_FROM(*pobject); if (thread) { switch((access=pthreads_modifiers_get(thread->modifiers, method TSRMLS_CC))){ case ZEND_ACC_PRIVATE: case ZEND_ACC_PROTECTED: scope = Z_OBJCE_PP(pobject); lcname = (char*) calloc(1, methodl+1); zend_str_tolower_copy(lcname, method, methodl); if (zend_hash_find(&scope->function_table, lcname, methodl+1, (void**)&call)==SUCCESS) { callable = (zend_function*) emalloc(sizeof(zend_function)); callable->type = ZEND_OVERLOADED_FUNCTION; callable->common.function_name = call->common.function_name; callable->common.fn_flags = ZEND_ACC_PUBLIC; callable->common.scope = scope; callable->common.arg_info = call->common.arg_info; callable->common.num_args = call->common.num_args; callable->common.required_num_args = call->common.required_num_args; #if PHP_VERSION_ID < 50400 callable->common.pass_rest_by_reference = call->common.pass_rest_by_reference; callable->common.return_reference = call->common.return_reference; #endif free(lcname); return callable; } free(lcname); /* TODO : if not found ? switch to default ? or return some error ? */ default: call = zend_handlers->get_method(PTHREADS_GET_METHOD_PASSTHRU_C); } } else call = zend_handlers->get_method(PTHREADS_GET_METHOD_PASSTHRU_C); return call; } /* }}} */
/* {{{ */ int pthreads_has_property(PTHREADS_HAS_PROPERTY_PASSTHRU_D) { int isset = 0; zend_long *guard = NULL; pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object)); cache = NULL; if (Z_OBJCE_P(object)->__isset && (guard = pthreads_get_guard(&threaded->std, member)) && !((*guard) & IN_ISSET)) { zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fcc = empty_fcall_info_cache; zval rv; ZVAL_UNDEF(&rv); fci.size = sizeof(zend_fcall_info); fci.retval = &rv; fci.object = &threaded->std; zend_fcall_info_argn(&fci, 1, member); fcc.initialized = 1; fcc.function_handler = Z_OBJCE_P(object)->__isset; fcc.object = &threaded->std; (*guard) |= IN_ISSET; zend_call_function(&fci, &fcc); (*guard) &= ~IN_ISSET; if (Z_TYPE(rv) != IS_UNDEF) { isset = zend_is_true(&rv); zval_dtor(&rv); } zend_fcall_info_args_clear(&fci, 1); } else { isset = pthreads_store_isset(object, member, has_set_exists); } return isset; }
/* {{{ */ void pthreads_unset_property(PTHREADS_UNSET_PROPERTY_PASSTHRU_D) { zend_long *guard = NULL; pthreads_object_t* threaded = PTHREADS_FETCH_FROM(Z_OBJ_P(object)); cache = NULL; rebuild_object_properties(&threaded->std); if (Z_OBJCE_P(object)->__unset && (guard = pthreads_get_guard(&threaded->std, member)) && !((*guard) & IN_UNSET)) { zend_fcall_info fci = empty_fcall_info; zend_fcall_info_cache fcc = empty_fcall_info_cache; zval rv; ZVAL_UNDEF(&rv); fci.size = sizeof(zend_fcall_info); fci.retval = &rv; fci.object = &threaded->std; zend_fcall_info_argn(&fci, 1, member); fcc.initialized = 1; fcc.function_handler = Z_OBJCE_P(object)->__unset; fcc.object = &threaded->std; (*guard) |= IN_UNSET; zend_call_function(&fci, &fcc); (*guard) &= ~IN_UNSET; if (Z_TYPE(rv) != IS_UNDEF) { zval_dtor(&rv); } zend_fcall_info_args_clear(&fci, 1); } else { if (pthreads_store_delete(object, member) == SUCCESS){ } } }
/* {{ reads a property from a thread, wherever it is available */ zval * pthreads_read_property (PTHREADS_READ_PROPERTY_PASSTHRU_D) { zval *value = NULL, *mstring = NULL; PTHREAD pthreads = PTHREADS_FETCH_FROM(object); if (Z_TYPE_P(member) != IS_STRING) { ALLOC_ZVAL(mstring); *mstring = *member; zval_copy_ctor( mstring ); INIT_PZVAL(mstring); convert_to_string(mstring); member = mstring; #if PHP_VERSION_ID > 50399 key = NULL; #endif } if (Z_TYPE_P(member)==IS_STRING) { pthreads_store_read(pthreads->store, Z_STRVAL_P(member), Z_STRLEN_P(member), &value TSRMLS_CC); } else { zend_error(E_WARNING, "pthreads detected an attempt to use an unsupported kind of key in %s", Z_OBJCE_P(object)->name); if (value != NULL) { value = EG( uninitialized_zval_ptr ); Z_ADDREF_P(value); } } if (mstring != NULL) { zval_ptr_dtor(&mstring); } return value; }
/* {{{ writes a property to a thread in the appropriate way */ void pthreads_write_property(PTHREADS_WRITE_PROPERTY_PASSTHRU_D) { PTHREAD pthreads = PTHREADS_FETCH_FROM(object); if (Z_TYPE_P(member)==IS_STRING) { switch(Z_TYPE_P(value)){ case IS_STRING: case IS_LONG: case IS_ARRAY: case IS_OBJECT: case IS_NULL: case IS_DOUBLE: case IS_BOOL: { if (pthreads_store_write(pthreads->store, Z_STRVAL_P(member), Z_STRLEN_P(member), &value TSRMLS_CC)!=SUCCESS){ zend_error_noreturn( E_WARNING, "pthreads failed to write member %s::$%s", Z_OBJCE_P(object)->name, Z_STRVAL_P(member) ); } else zend_handlers->write_property(PTHREADS_WRITE_PROPERTY_PASSTHRU_C); } break; default: zend_handlers->write_property(PTHREADS_WRITE_PROPERTY_PASSTHRU_C); } } else zend_handlers->write_property(PTHREADS_WRITE_PROPERTY_PASSTHRU_C); }
/* {{{ writes a property to a thread in the appropriate way */ void pthreads_write_property(PTHREADS_WRITE_PROPERTY_PASSTHRU_D) { PTHREAD pthreads = PTHREADS_FETCH_FROM(object); zval *mstring = NULL; zend_bool nulled = 0; zend_bool locked; if (member == NULL || Z_TYPE_P(member) == IS_NULL) { /* for anonymous members, we acquire the lock and increment a counter we do not store any additional information or perform any lookups */ pthreads_lock_acquire(pthreads->store->lock, &locked TSRMLS_CC); { if (member == NULL) { MAKE_STD_ZVAL(member); nulled = 1; } ZVAL_LONG(member, pthreads->store->next++); } pthreads_lock_release(pthreads->store->lock, locked TSRMLS_CC); } if (Z_TYPE_P(member) != IS_STRING) { ALLOC_ZVAL(mstring); *mstring = *member; zval_copy_ctor( mstring ); INIT_PZVAL(mstring); convert_to_string(mstring); if(nulled) FREE_ZVAL(member); member = mstring; #if PHP_VERSION_ID > 50399 key = NULL; #endif } if (Z_TYPE_P(member)==IS_STRING) { switch(Z_TYPE_P(value)){ case IS_STRING: case IS_LONG: case IS_ARRAY: case IS_OBJECT: case IS_NULL: case IS_DOUBLE: case IS_RESOURCE: case IS_BOOL: { if (pthreads_store_write(pthreads->store, Z_STRVAL_P(member), Z_STRLEN_P(member), &value TSRMLS_CC)!=SUCCESS){ zend_error( E_WARNING, "pthreads failed to write member %s::$%s", Z_OBJCE_P(object)->name, Z_STRVAL_P(member) ); } } break; default: { zend_error( E_WARNING, "pthreads detected an attempt to use an unsupported kind of data for %s::$%s", Z_OBJCE_P(object)->name, Z_STRVAL_P(member) ); } } } else zend_error( E_WARNING, "pthreads detected an attempt to use an unsupported kind of key in %s", Z_OBJCE_P(object)->name ); if (mstring != NULL) { zval_ptr_dtor(&mstring); } }
if (pthreads_lock_acquire(thread->lock, &locked TSRMLS_CC)) { zend_llist *stack = &thread->stack->objects; if (work) { zend_llist_del_element(stack, &work, (int (*)(void *, void *)) pthreads_equal_func); } else zend_llist_destroy(stack); remain = stack->count; pthreads_lock_release(thread->lock, locked TSRMLS_CC); } else remain = -1; return remain; } /* }}} */ /* {{{ push an item onto the work buffer */ size_t pthreads_stack_push(PTHREAD thread, zval *work TSRMLS_DC) { zend_bool locked; PTHREAD stackable = PTHREADS_FETCH_FROM(work); size_t counted = 0L; if (pthreads_lock_acquire(thread->lock, &locked TSRMLS_CC)) { zend_llist *stack = &thread->stack->objects; if (stack) { zend_llist_add_element( stack, &stackable ); counted = stack->count; Z_OBJ_HT_P(work)->add_ref(work TSRMLS_CC); } pthreads_lock_release(thread->lock, locked TSRMLS_CC); if (counted > 0L) {
zend_hash_get_current_data_ex(stack, (void**)&search, &position) == SUCCESS; zend_hash_move_forward_ex(stack, &position)) { /* arghhh */ } } else zend_hash_destroy(stack); remain = zend_hash_num_elements(stack); } pthreads_lock_release(thread->lock, locked TSRMLS_CC); } else remain = -1; return remain; } /* }}} */ /* {{{ push an item onto the work buffer */ size_t pthreads_stack_push(PTHREAD thread, zval *work TSRMLS_DC) { zend_bool locked; PTHREAD threaded = PTHREADS_FETCH_FROM(work); size_t counted = 0L; if (pthreads_lock_acquire(thread->lock, &locked TSRMLS_CC)) { HashTable *stack = &thread->stack->objects; if (stack) { if (!zend_hash_num_elements(stack)) { zend_hash_clean( stack); thread->stack->position = 0L; } zend_hash_next_index_insert( stack, (void**) &threaded, sizeof(struct _pthread_construct), NULL ); counted = zend_hash_num_elements(stack);
if (zend_hash_init(&store->table, 8, NULL, (dtor_func_t) pthreads_store_storage_dtor, 1)==SUCCESS) { if ((store->lock = pthreads_lock_alloc(TSRMLS_C))) { store->next = 0L; return store; } zend_hash_destroy(&store->table); } free(store); } return NULL; } /* }}} */ /* {{{ lock storage, userland only */ zend_bool pthreads_store_lock(zval *this_ptr TSRMLS_DC) { PTHREAD pobject = PTHREADS_FETCH_FROM(getThis()); if (pobject) { return pthreads_lock_acquire( pobject->store->lock, &pobject->hold TSRMLS_CC ); } else return 0; } /* }}} */ /* {{{ unlock storage, userland only */ zend_bool pthreads_store_unlock(zval *this_ptr TSRMLS_DC) { PTHREAD pobject = PTHREADS_FETCH_FROM(getThis()); if (pobject) { return pthreads_lock_release( pobject->store->lock, pobject->hold TSRMLS_CC