def run_concurrent_readers_test(allocator_type): print('-- [%s] concurrent readers' % allocator_type) table = sharedstructures.HashTable('test-table', allocator_type) del table child_pids = set() while (len(child_pids) < 8) and (0 not in child_pids): child_pids.add(os.fork()) if 0 in child_pids: # child process: try up to a second to get the key table = sharedstructures.HashTable('test-table', allocator_type) value = 100 start_time = int(time.time() * 1000000) while (value < 110) and \ (int(time.time() * 1000000) < (start_time + 1000000)): time.sleep(0.001) try: res = table[b'key1'] except KeyError: pass else: if res == value: value += 1 os._exit(int(value != 110)) else: # parent process: write the key, then wait for children to terminate table = sharedstructures.HashTable('test-table', allocator_type) for value in range(100, 110): time.sleep(0.05) table[b'key1'] = value num_failures = 0 while child_pids: pid, exit_status = os.wait() child_pids.remove(pid) if os.WIFEXITED(exit_status) and (os.WEXITSTATUS(exit_status) == 0): print('-- [%s] child %d terminated successfully' % ( allocator_type, pid)) else: print('-- [%s] child %d failed (%d)' % ( allocator_type, pid, exit_status)) num_failures += 1 assert 0 == len(child_pids) assert 0 == num_failures
def run_collision_test(allocator_type): print("-- [%s] collision" % allocator_type) table = sharedstructures.HashTable(POOL_NAME_PREFIX + allocator_type, allocator_type, 0, 2) expected = {} def insert_both(e, t, k, v): t[k] = v e[k] = v def delete_both(e, t, k): del t[k] del e[k] # writing 5 keys to a 4-slot hashtable forces a collision assert 2 == table.bits() assert 0 == len(table) insert_both(expected, table, b'key1', b'value1') insert_both(expected, table, b'key2', b'value2') insert_both(expected, table, b'key3', b'value3') insert_both(expected, table, b'key4', b'value4') insert_both(expected, table, b'key5', b'value5') verify_state(expected, table) while expected: k, _ = expected.popitem() del table[k] verify_state(expected, table)
def run_basic_test(allocator_type): print("-- [%s] basic" % allocator_type) before_lsof_count = len(get_current_process_lsof().splitlines()) table = sharedstructures.HashTable(POOL_NAME_PREFIX + allocator_type, allocator_type) expected = {} def insert_both(e, t, k, v): t[k] = v e[k] = v def delete_both(e, t, k): del t[k] del e[k] verify_state(expected, table) insert_both(expected, table, b'key1', b'value1') verify_state(expected, table) insert_both(expected, table, b'key2', b'value2') verify_state(expected, table) insert_both(expected, table, b'key3', b'value3') verify_state(expected, table) delete_both(expected, table, b'key2') verify_state(expected, table) try: del table[b'key2'] assert False, "del table[\'key2\'] did not raise KeyError" except KeyError: pass verify_state(expected, table) insert_both(expected, table, b'key1', b'value0') verify_state(expected, table) delete_both(expected, table, b'key1') verify_state(expected, table) delete_both(expected, table, b'key3') verify_state(expected, table) assert {} == expected del table # this should unmap the shared memory pool and close the fd sharedstructures.delete_pool(POOL_NAME_PREFIX + allocator_type) # this will fail if the test prints anything after before_lsof is taken since # the stdout/stderr offsets will be different assert before_lsof_count == len(get_current_process_lsof().splitlines())
def run_conditional_writes_test(allocator_type): print("-- [%s] conditional writes" % allocator_type) table = sharedstructures.HashTable(POOL_NAME_PREFIX + allocator_type, allocator_type) expected = {} def insert_both(e, t, k, v): t[k] = v e[k] = v def delete_both(e, t, k): del t[k] del e[k] def conditional_insert_both(e, t, check_k, check_v, target_k, target_v, written): if t.check_and_set(check_k, check_v, target_k, target_v): e[target_k] = target_v assert written else: assert not written def conditional_missing_insert_both(e, t, check_k, target_k, target_v, written): if t.check_missing_and_set(check_k, target_k, target_v): e[target_k] = target_v assert written else: assert not written def conditional_delete_both(e, t, check_k, check_v, target_k, written): if t.check_and_set(check_k, check_v, target_k): del e[target_k] assert written else: assert not written def conditional_missing_delete_both(e, t, check_k, target_k, written): if t.check_missing_and_set(check_k, target_k): del e[target_k] assert written else: assert not written verify_state(expected, table) insert_both(expected, table, b"key1", b"value1") verify_state(expected, table) insert_both(expected, table, b"key2", b"value2") verify_state(expected, table) # check that conditions on the same key work conditional_insert_both(expected, table, b"key1", b"value2", b"key1", b"value1_1", False) verify_state(expected, table) conditional_insert_both(expected, table, b"key1", b"value1", b"key1", b"value1_1", True) verify_state(expected, table) # check that conditions on other keys work conditional_insert_both(expected, table, b"key2", b"value1", b"key1", b"value1", False) verify_state(expected, table) conditional_insert_both(expected, table, b"key2", b"value2", b"key1", b"value1", True) verify_state(expected, table) # check that missing conditions work conditional_missing_insert_both(expected, table, b"key2", b"key3", b"value3", False) verify_state(expected, table) conditional_missing_insert_both(expected, table, b"key3", b"key3", b"value3", True) verify_state(expected, table) # check that conditional deletes work conditional_delete_both(expected, table, b"key1", b"value2", b"key1", False) verify_state(expected, table) conditional_delete_both(expected, table, b"key1", b"value1", b"key1", True) verify_state(expected, table) conditional_missing_delete_both(expected, table, b"key3", b"key2", False) verify_state(expected, table) conditional_missing_delete_both(expected, table, b"key1", b"key2", True) verify_state(expected, table) delete_both(expected, table, b"key3") assert expected == {}
def run_incr_test(allocator_type): print('-- [%s] incr' % allocator_type) table = sharedstructures.HashTable(POOL_NAME_PREFIX + allocator_type, allocator_type, 0, 6) expected = {} def insert_both(k, v): table[k] = v expected[k] = v def delete_both(k): del table[k] del expected[k] # giving garbage to incr() should cause a TypeError try: table.incr(b'missing', b'not a number, lolz') assert False except TypeError: pass try: table.incr(b'missing', {'still': 'not', 'a': 'number'}) assert False except TypeError: pass assert 0 == len(table) insert_both(b'int8', struct.pack(b'@b', 40)) insert_both(b'int16', struct.pack(b'@h', 4000)) insert_both(b'int32', struct.pack(b'@l', 60000000)) insert_both(b'int64', struct.pack(b'@q', 800000000000000)) insert_both(b'float', struct.pack(b'@f', 10.0)) insert_both(b'double', struct.pack(b'@d', 15.5)) insert_both(b'string', b'7 bytes') assert 7 == len(table) # incr should create the key if it doesn't exist assert -10 == table.incr(b"int8-2", -10) assert -4000 == table.incr(b"int16-2", -4000) assert -60000000 == table.incr(b"int32-2", -60000000) assert -800000000000000 == table.incr(b"int64-2", -800000000000000) assert -10.0 == table.incr(b"float-2", -10.0) assert -15.5 == table.incr(b"double-2", -15.5) assert 13 == len(table) # all the keys should have the values we set, but the keys created by incr # should all be 64 bits assert struct.pack(b'@b', 40) == table[b"int8"] assert struct.pack(b'@h', 4000) == table[b"int16"] assert struct.pack(b'@l', 60000000) == table[b"int32"] assert struct.pack(b'@q', 800000000000000) == table[b"int64"] assert struct.pack(b'@f', 10.0) == table[b"float"] assert struct.pack(b'@d', 15.5) == table[b"double"] assert struct.pack(b'@q', -10) == table[b"int8-2"] assert struct.pack(b'@q', -4000) == table[b"int16-2"] assert struct.pack(b'@q', -60000000) == table[b"int32-2"] assert struct.pack(b'@q', -800000000000000) == table[b"int64-2"] assert struct.pack(b'@d', -10.0) == table[b"float-2"] assert struct.pack(b'@d', -15.5) == table[b"double-2"] assert 13 == table.size() # incr should return the new value of the key assert struct.pack(b'@b', 44) == table.incr(b"int8", 4) assert struct.pack(b'@h', 4010) == table.incr(b"int16", 10) assert struct.pack(b'@l', 60000100) == table.incr(b"int32", 100) assert struct.pack(b'@q', 800000000001000) == table.incr(b"int64", 1000) assert struct.pack(b'@f', 30.0) == table.incr(b"float", 20.0) assert struct.pack(b'@d', 25.5) == table.incr(b"double", 10.0) assert struct.pack(b'@q', -14) == table.incr(b"int8-2", -4) assert struct.pack(b'@q', -4010) == table.incr(b"int16-2", -10) assert struct.pack(b'@q', -60000100) == table.incr(b"int32-2", -100) assert struct.pack(b'@q', -800000000001000) == table.incr(b"int64-2", -1000) assert struct.pack(b'@d', -30.0) == table.incr(b"float-2", -20.0) assert struct.pack(b'@d', -25.5) == table.incr(b"double-2", -10.0) assert 13 == table.size() # test incr() on keys of the wrong size try: table.incr(b"string", 14) assert False except ValueError: pass try: table.incr(b"string", 15.0) assert False except ValueError: pass # we're done here table.clear() assert len(table) == 0