From 880ad0ff67ad5d9ef5ae505ea956c1698777b121 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Tue, 8 Oct 2019 12:58:21 +0200 Subject: [PATCH] Implement apcu_inc/dec using atomic operations This also changes apcu_inc/dec to wrap around on overflow, instead of saturating to a float value. --- apc_lock_api.h | 3 +++ php_apc.c | 31 ++++++++++++------------------- tests/apc_012.phpt | 37 ++++++++++++++++++------------------- 3 files changed, 33 insertions(+), 38 deletions(-) diff --git a/apc_lock_api.h b/apc_lock_api.h index 5686a55f..2d62935e 100644 --- a/apc_lock_api.h +++ b/apc_lock_api.h @@ -98,15 +98,18 @@ PHP_APCU_API void apc_lock_destroy(apc_lock_t *lock); /* }}} */ # ifdef _WIN64 # define ATOMIC_INC(a) InterlockedIncrement64(&a) # define ATOMIC_DEC(a) InterlockedDecrement64(&a) +# define ATOMIC_ADD(a, b) (InterlockedExchangeAdd64(&a, b) + b) # define ATOMIC_CAS(a, old, new) (InterlockedCompareExchange64(&a, new, old) == old) # else # define ATOMIC_INC(a) InterlockedIncrement(&a) # define ATOMIC_DEC(a) InterlockedDecrement(&a) +# define ATOMIC_ADD(a, b) (InterlockedExchangeAdd(&a, b) + b) # define ATOMIC_CAS(a, old, new) (InterlockedCompareExchange(&a, new, old) == old) # endif #else # define ATOMIC_INC(a) __sync_add_and_fetch(&a, 1) # define ATOMIC_DEC(a) __sync_sub_and_fetch(&a, 1) +# define ATOMIC_ADD(a, b) __sync_add_and_fetch(&a, b) # define ATOMIC_CAS(a, old, new) __sync_bool_compare_and_swap(&a, old, new) #endif diff --git a/php_apc.c b/php_apc.c index 8de09c74..ad9351d9 100644 --- a/php_apc.c +++ b/php_apc.c @@ -432,7 +432,7 @@ PHP_FUNCTION(apcu_sma_info) /* {{{ php_apc_update */ zend_bool php_apc_update( - zend_string *key, apc_cache_updater_t updater, void *data, + zend_string *key, apc_cache_atomic_updater_t updater, void *data, zend_bool insert_if_not_found, time_t ttl) { if (APCG(serializer_name)) { @@ -440,7 +440,7 @@ zend_bool php_apc_update( apc_cache_serializer(apc_user_cache, APCG(serializer_name)); } - return apc_cache_update(apc_user_cache, key, updater, data, insert_if_not_found, ttl); + return apc_cache_atomic_update_long(apc_user_cache, key, updater, data, insert_if_not_found, ttl); } /* }}} */ @@ -521,20 +521,14 @@ PHP_FUNCTION(apcu_add) { /* {{{ php_inc_updater */ struct php_inc_updater_args { - zval step; - zval rval; + zend_long step; + zend_long rval; }; -static zend_bool php_inc_updater(apc_cache_t* cache, apc_cache_entry_t* entry, void* data) { - struct php_inc_updater_args *args = (struct php_inc_updater_args*) data; - - if (Z_TYPE(entry->val) == IS_LONG) { - fast_long_add_function(&entry->val, &entry->val, &args->step); - ZVAL_COPY_VALUE(&args->rval, &entry->val); - return 1; - } - - return 0; +static zend_bool php_inc_updater(apc_cache_t *cache, zend_long *entry, void *data) { + struct php_inc_updater_args *args = (struct php_inc_updater_args *) data; + args->rval = ATOMIC_ADD(*entry, args->step); + return 1; } /* {{{ proto long apcu_inc(string key [, long step [, bool& success [, long ttl]]]) @@ -549,12 +543,12 @@ PHP_FUNCTION(apcu_inc) { return; } - ZVAL_LONG(&args.step, step); + args.step = step; if (php_apc_update(key, php_inc_updater, &args, 1, ttl)) { if (success) { ZEND_TRY_ASSIGN_REF_TRUE(success); } - RETURN_ZVAL(&args.rval, 0, 0); + RETURN_LONG(args.rval); } if (success) { @@ -577,14 +571,13 @@ PHP_FUNCTION(apcu_dec) { return; } - ZVAL_LONG(&args.step, 0 - step); - + args.step = 0 - step; if (php_apc_update(key, php_inc_updater, &args, 1, ttl)) { if (success) { ZEND_TRY_ASSIGN_REF_TRUE(success); } - RETURN_ZVAL(&args.rval, 0, 0); + RETURN_LONG(args.rval); } if (success) { diff --git a/tests/apc_012.phpt b/tests/apc_012.phpt index 16dc25ff..8484112d 100644 --- a/tests/apc_012.phpt +++ b/tests/apc_012.phpt @@ -1,5 +1,5 @@ --TEST-- -APC: integer overflow consistency +APC: Atomic inc + dec wrap around on overflow --SKIPIF-- --INI-- @@ -7,30 +7,29 @@ apc.enabled=1 apc.enable_cli=1 --FILE-- ===DONE=== --EXPECTF-- -int(%d) +int(%i) +int(%i) +bool(true) bool(true) -float(%s) +int(%i) +int(%i) bool(true) -float(%s) bool(true) ===DONE===