diff --git a/.gitignore b/.gitignore index 4a9fcd2a7..42d497398 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ generated cmake-build-debug cmake-build-release cmake-build-debug-coverage +cmake-build-* /CMakeUserPresets.json /ConanPresets.json @@ -43,3 +44,14 @@ __pycache__ *.log *.pdf *.run.xml + +CMakeCache.txt +CMakeFiles/ +CPackConfig.cmake +CPackSourceConfig.cmake +CTestTestfile.cmake +DartConfiguration.tcl +Makefile +cmake_install.cmake +*.cmake +*.tcl \ No newline at end of file diff --git a/sc-memory/sc-core/src/sc-store/sc-base/sc_thread.c b/sc-memory/sc-core/src/sc-store/sc-base/sc_thread.c new file mode 100644 index 000000000..01d91a64b --- /dev/null +++ b/sc-memory/sc-core/src/sc-store/sc-base/sc_thread.c @@ -0,0 +1,35 @@ +/* + * This source file is part of an OSTIS project. For the latest info, see http://ostis.net + * Distributed under the MIT License + * (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) + */ + +#include "sc_thread.h" + +#include "sc-core/sc_types.h" + +sc_thread * sc_thread_new(sc_thread_name * name, sc_thread_func func, sc_thread_data data) +{ + return g_thread_new(name, func, data); +} + +void sc_thread_join(sc_thread * thread) +{ + if (thread != null_ptr) + { + g_thread_join(thread); + } +} + +void sc_thread_unref(sc_thread * thread) +{ + if (thread != null_ptr) + { + g_thread_unref(thread); + } +} + +void sc_thread_sleep(sc_uint32 milliseconds) +{ + g_usleep((gulong)milliseconds * 1000); +} diff --git a/sc-memory/sc-core/src/sc-store/sc-base/sc_thread.h b/sc-memory/sc-core/src/sc-store/sc-base/sc_thread.h index 6815d08da..d2aa0569b 100644 --- a/sc-memory/sc-core/src/sc-store/sc-base/sc_thread.h +++ b/sc-memory/sc-core/src/sc-store/sc-base/sc_thread.h @@ -7,10 +7,20 @@ #ifndef _sc_thread_h_ #define _sc_thread_h_ +#include "sc-core/sc_types.h" + #include typedef GThread sc_thread; +typedef gpointer sc_thread_data; +typedef gpointer (*sc_thread_func)(sc_thread_data); +typedef gchar const sc_thread_name; #define sc_thread_self g_thread_self +sc_thread * sc_thread_new(sc_thread_name * name, sc_thread_func func, sc_thread_data data); +void sc_thread_join(sc_thread * thread); +void sc_thread_unref(sc_thread * thread); +void sc_thread_sleep(sc_uint32 milliseconds); + #endif diff --git a/sc-memory/sc-core/src/sc-store/sc-transaction/sc_element_version.c b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_element_version.c new file mode 100644 index 000000000..f023d1028 --- /dev/null +++ b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_element_version.c @@ -0,0 +1,31 @@ +#include "sc_element_version.h" + +#include "sc-store/sc_version_segment.h" + +#include + +sc_element_data * sc_element_data_new() +{ + return sc_mem_new(sc_element_data, 1); +} + +sc_element_version * sc_element_create_new_version( + sc_element const * element, + sc_element_data const * new_element_data, + sc_uint64 const transaction_id) +{ + if (new_element_data == null_ptr) + return null_ptr; + + sc_element_version * new_version = sc_mem_new(sc_element_version, 1); + if (new_version == null_ptr) + return null_ptr; + + new_version->data = new_element_data; + new_version->transaction_id = transaction_id; + new_version->version_id = sc_version_segment_get_next_version_id(element); + new_version->parent_version = element->version_history->current_version; + new_version->is_committed = SC_FALSE; + + return new_version; +} diff --git a/sc-memory/sc-core/src/sc-store/sc-transaction/sc_element_version.h b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_element_version.h new file mode 100644 index 000000000..3334ba310 --- /dev/null +++ b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_element_version.h @@ -0,0 +1,39 @@ +#ifndef SC_ELEMENT_VERSION_H +#define SC_ELEMENT_VERSION_H + +#include "sc-core/sc_types.h" +#include "sc-store/sc_element.h" + +typedef struct sc_element_data +{ + sc_element_flags flags; + + sc_addr first_out_arc; + sc_addr first_in_arc; +#ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES + sc_addr first_in_arc_from_structure; +#endif + + sc_arc_info arc; + + sc_uint32 incoming_arcs_count; + sc_uint32 outgoing_arcs_count; +} sc_element_data; + +sc_element_data * sc_element_data_new(); + +typedef struct sc_element_version +{ + sc_element_data const * data; + sc_uint64 version_id; + sc_uint64 transaction_id; + struct sc_element_version * parent_version; + sc_bool is_committed; // redundant? +} sc_element_version; + +sc_element_version * sc_element_create_new_version( + sc_element const * element, + sc_element_data const * new_element_data, + sc_uint64 transaction_id); + +#endif diff --git a/sc-memory/sc-core/src/sc-store/sc-transaction/sc_memory_transaction_manager.c b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_memory_transaction_manager.c new file mode 100644 index 000000000..e76865011 --- /dev/null +++ b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_memory_transaction_manager.c @@ -0,0 +1,323 @@ +#include "sc_memory_transaction_manager.h" + +#include "sc-store/sc_storage.h" +#include "sc-store/sc-base/sc_condition_private.h" +#include "sc-store/sc-base/sc_monitor_table.h" +#include "sc-store/sc-base/sc_monitor_table_private.h" +#include "sc-store/sc-base/sc_thread.h" +#include "sc-store/sc-container/sc_struct_node.h" + +#include + +sc_memory_transaction_manager * transaction_manager = null_ptr; + +sc_result sc_memory_transaction_manager_initialize(sc_memory_transaction_manager * manager) +{ + if (sc_memory_transaction_manager_is_initialized()) + { + return SC_RESULT_OK; + } + + transaction_manager = manager; + + if (transaction_manager == null_ptr) + { + return SC_RESULT_ERROR_INVALID_PARAMS; + } + + transaction_manager->should_stop = SC_FALSE; + transaction_manager->threads = null_ptr; + transaction_manager->txn_count = 0; + + transaction_manager->transaction_queue = sc_mem_new(sc_queue, 1); + if (transaction_manager->transaction_queue == null_ptr) + { + return SC_RESULT_ERROR_FULL_MEMORY; + } + sc_queue_init(transaction_manager->transaction_queue); + + transaction_manager->monitor = sc_mem_new(sc_monitor, 1); + if (transaction_manager->monitor == null_ptr) + { + goto error_cleanup_queue; + } + sc_monitor_init(transaction_manager->monitor); + + transaction_manager->queue_condition = sc_mem_new(sc_condition, 1); + if (transaction_manager->queue_condition == null_ptr) + { + goto error_cleanup_monitor; + } + sc_cond_init(transaction_manager->queue_condition); + + transaction_manager->mutex = sc_mem_new(sc_mutex, 1); + if (transaction_manager->mutex == null_ptr) + { + goto error_cleanup_condition; + } + sc_mutex_init(transaction_manager->mutex); + + transaction_manager->monitor_table = sc_mem_new(sc_monitor_table, 1); + if (transaction_manager->monitor_table == null_ptr) + { + goto error_cleanup_mutex; + } + _sc_monitor_table_init(transaction_manager->monitor_table); + + transaction_manager->processed_elements = sc_hash_table_init( + sc_hash_table_default_hash_func, sc_hash_table_default_equal_func, null_ptr, null_ptr); + if (transaction_manager->processed_elements == null_ptr) + { + goto error_cleanup_monitor_table; + } + + if (sc_list_init(&transaction_manager->threads) != SC_RESULT_OK) + { + goto error_cleanup_hash_table; + } + + for (int i = 0; i < SC_TRANSACTION_THREAD_COUNT; ++i) + { + sc_thread * thread = sc_thread_new("sc_transaction_handler", sc_transaction_handler, 0); + if (thread == null_ptr) + { + goto error_cleanup_threads; + } + sc_list_push_back(transaction_manager->threads, thread); + } + + return SC_RESULT_OK; + +error_cleanup_threads: + if (transaction_manager->threads != null_ptr) + { + for (sc_struct_node const * it = transaction_manager->threads->begin; it != null_ptr; it = it->next) + { + sc_thread_unref(it->data); + } + sc_list_destroy(transaction_manager->threads); + transaction_manager->threads = null_ptr; + } + +error_cleanup_hash_table: + sc_hash_table_destroy(transaction_manager->processed_elements); + transaction_manager->processed_elements = null_ptr; + +error_cleanup_monitor_table: + sc_mem_free(transaction_manager->monitor_table); + transaction_manager->monitor_table = null_ptr; + +error_cleanup_mutex: + sc_mutex_destroy(transaction_manager->mutex); + sc_mem_free(transaction_manager->mutex); + +error_cleanup_condition: + sc_cond_destroy(transaction_manager->queue_condition); + sc_mem_free(transaction_manager->queue_condition); + +error_cleanup_monitor: + sc_monitor_destroy(transaction_manager->monitor); + sc_mem_free(transaction_manager->monitor); + +error_cleanup_queue: + sc_queue_destroy(transaction_manager->transaction_queue); + sc_mem_free(transaction_manager->transaction_queue); + + transaction_manager = null_ptr; + + return SC_RESULT_ERROR; +} + +sc_bool sc_memory_transaction_manager_is_initialized() +{ + return transaction_manager != null_ptr; +} + +void sc_memory_transaction_shutdown() +{ + if (transaction_manager != null_ptr) + { + transaction_manager->should_stop = SC_TRUE; + sc_transaction_manager_destroy(); + } +} + +sc_memory_transaction_manager * sc_memory_transaction_manager_get() +{ + return transaction_manager; +} + +void sc_transaction_manager_destroy() +{ + if (transaction_manager == null_ptr) + return; + + transaction_manager->should_stop = SC_TRUE; + + sc_cond_broadcast(transaction_manager->queue_condition); + + if (transaction_manager->threads != null_ptr) + { + for (sc_struct_node const * it = transaction_manager->threads->begin; it != null_ptr; it = it->next) + { + if (it->data != null_ptr) + { + sc_thread * thread = it->data; + sc_thread_join(thread); + sc_thread_unref(thread); + } + } + sc_list_destroy(transaction_manager->threads); + transaction_manager->threads = null_ptr; + } + + if (transaction_manager->transaction_queue != null_ptr) + { + sc_queue_destroy(transaction_manager->transaction_queue); + sc_mem_free(transaction_manager->transaction_queue); + transaction_manager->transaction_queue = null_ptr; + } + + if (transaction_manager->monitor != null_ptr) + { + sc_monitor_destroy(transaction_manager->monitor); + sc_mem_free(transaction_manager->monitor); + transaction_manager->monitor = null_ptr; + } + + sc_cond_destroy(transaction_manager->queue_condition); + sc_mutex_destroy(transaction_manager->mutex); + + sc_mem_free(transaction_manager); + transaction_manager = null_ptr; +} + +sc_transaction * sc_memory_transaction_new(sc_memory_context * ctx) +{ + if (!sc_memory_transaction_manager_is_initialized()) + { + return null_ptr; + } + + sc_monitor_acquire_write(transaction_manager->monitor); + sc_uint64 const txn_id = transaction_manager->txn_count++; + sc_monitor_release_write(transaction_manager->monitor); + + return sc_transaction_new(txn_id, ctx); +} + +sc_result sc_memory_transaction_commit(sc_transaction * txn) +{ + if (txn == null_ptr) + return SC_RESULT_ERROR_INVALID_PARAMS; + + sc_transaction_manager_transaction_add(txn); + return SC_RESULT_OK; +} + +void sc_transaction_manager_transaction_add(sc_transaction * txn) +{ + sc_monitor_acquire_write(transaction_manager->monitor); + + sc_queue_push(transaction_manager->transaction_queue, txn); + + sc_cond_signal(transaction_manager->queue_condition); + + sc_monitor_release_write(transaction_manager->monitor); +} + +void sc_transaction_manager_block_elements(sc_hash_table * elements) +{ + // block with monitor table +} + +void sc_transaction_manager_unblock_elements(sc_hash_table * elements) +{ + // unlock +} + +void sc_transaction_manager_transaction_execute(sc_transaction * txn) +{ + if (!sc_transaction_elements_intersect(txn, transaction_manager->processed_elements)) + { + sc_transaction_manager_block_elements(txn->elements); + + if (sc_transaction_validate(txn)) + { + sc_transaction_apply(txn); + } + + sc_transaction_manager_unblock_elements(txn->elements); + } + else + txn->state = SC_TRANSACTION_FAILED; +} + +void sc_transaction_handler() +{ + while (!transaction_manager->should_stop) + { + sc_monitor_acquire_write(transaction_manager->monitor); + + if (transaction_manager->transaction_queue->size == 0) + { + sc_monitor_release_write(transaction_manager->monitor); + sc_mutex_lock(transaction_manager->mutex); + sc_cond_wait(transaction_manager->queue_condition, transaction_manager->mutex); + sc_mutex_unlock(transaction_manager->mutex); + continue; + } + + if (!transaction_manager->should_stop && transaction_manager->transaction_queue->size > 0) + { + sc_transaction * txn = sc_queue_front(transaction_manager->transaction_queue); + sc_queue_pop(transaction_manager->transaction_queue); + + sc_monitor_release_write(transaction_manager->monitor); + + sc_transaction_manager_transaction_execute(txn); + + txn->state = SC_TRANSACTION_EXECUTED; + } + else + { + sc_monitor_release_write(transaction_manager->monitor); + } + } + + sc_monitor_release_read(transaction_manager->monitor); +} + +sc_bool sc_transaction_elements_intersect(sc_transaction const * txn, sc_hash_table * processed_elements) +{ + if (txn == null_ptr || txn->elements == null_ptr || processed_elements == null_ptr) + return SC_FALSE; + + sc_hash_table * smaller_table = + sc_hash_table_size(txn->elements) <= sc_hash_table_size(processed_elements) ? txn->elements : processed_elements; + sc_hash_table * larger_table = smaller_table == txn->elements ? processed_elements : txn->elements; + + sc_monitor * txn_monitor = txn->monitor; + sc_monitor * processed_monitor = sc_memory_transaction_manager_get()->monitor; + + sc_monitor_acquire_read(txn_monitor); + sc_monitor_acquire_read(processed_monitor); + + sc_hash_table_iterator iter; + sc_hash_table_iterator_init(&iter, smaller_table); + void *key, *value; + + while (sc_hash_table_iterator_next(&iter, &key, &value)) + { + if (sc_hash_table_get(larger_table, key) != null_ptr) + { + sc_monitor_release_read(txn_monitor); + sc_monitor_release_read(processed_monitor); + return SC_TRUE; + } + } + + sc_monitor_release_read(txn_monitor); + sc_monitor_release_read(processed_monitor); + return SC_FALSE; +} \ No newline at end of file diff --git a/sc-memory/sc-core/src/sc-store/sc-transaction/sc_memory_transaction_manager.h b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_memory_transaction_manager.h new file mode 100644 index 000000000..5f2eb0e82 --- /dev/null +++ b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_memory_transaction_manager.h @@ -0,0 +1,55 @@ +#ifndef SC_TRANSACTION_MANAGER_H +#define SC_TRANSACTION_MANAGER_H + +#include "sc-store/sc-container/sc_hash_table.h" +#include +#include + +// TODO: determinate amount +#define SC_TRANSACTION_THREAD_COUNT 2 +#define SC_TRANSACTION_MANAGER_QUEUE_SIZE 10 + +typedef struct sc_memory_transaction_manager +{ + sc_monitor_table * monitor_table; + sc_queue * transaction_queue; + sc_monitor * monitor; + sc_mutex * mutex; + sc_list * threads; + + sc_condition * queue_condition; + sc_uint32 txn_count; + sc_bool should_stop; + + sc_hash_table * processed_elements; +} sc_memory_transaction_manager; + +sc_result sc_memory_transaction_manager_initialize(sc_memory_transaction_manager * manager); +// initialize the transaction manager +sc_bool sc_memory_transaction_manager_is_initialized(); +// check if the transaction manager is initialized +void sc_memory_transaction_shutdown(); +// shutdown the transaction manager + +sc_memory_transaction_manager * sc_memory_transaction_manager_get(); +// return transaction manager + +sc_transaction * sc_memory_transaction_new(sc_memory_context * ctx); +// create a new empty sc-transaction +sc_result sc_memory_transaction_commit(sc_transaction * txn); +// try to commit transaction (queue->execute->commit) + +void sc_transaction_manager_transaction_add(sc_transaction * txn); +// add transaction to the queue and start operations when the thread is ready +void sc_transaction_manager_transaction_execute(sc_transaction * txn); +// execute the transaction and wait till it's finished + +void sc_transaction_handler(); +// method for transaction queue processing by threads + +void sc_transaction_manager_destroy(); +// destroy the transaction manager + +sc_bool sc_transaction_elements_intersect(sc_transaction const * txn, sc_hash_table * processed_elements); + +#endif diff --git a/sc-memory/sc-core/src/sc-store/sc-transaction/sc_memory_transaction_operations.c b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_memory_transaction_operations.c new file mode 100644 index 000000000..e9efedd1f --- /dev/null +++ b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_memory_transaction_operations.c @@ -0,0 +1,144 @@ +#include "sc_memory_transaction_operations.h" + +#include "sc_memory_transaction_manager.h" +#include "sc-core/sc_memory.h" +#include "sc-store/sc_storage_private.h" + +sc_addr sc_memory_transaction_node_new(sc_transaction const * txn, sc_type const type) +{ + if (txn == null_ptr) + return SC_ADDR_EMPTY; + + sc_result result; + sc_addr const allocated_addr = sc_memory_node_new_ext(txn->ctx, type, &result); + sc_transaction_element_new(txn, &allocated_addr); + + return allocated_addr; +} + +sc_addr sc_memory_transaction_link_new(sc_transaction const * txn) +{ + if (txn == null_ptr) + return SC_ADDR_EMPTY; + + sc_result result; + sc_addr const allocated_addr = sc_memory_link_new_ext(txn->ctx, sc_type_const_node_link, &result); + sc_transaction_element_new(txn, &allocated_addr); + + return allocated_addr; +} + +sc_addr sc_memory_transaction_arc_new( + sc_transaction const * txn, + sc_type const type, + sc_addr * const beg_addr, + sc_addr * const end_addr) +{ + if (txn == null_ptr) + return SC_ADDR_EMPTY; + + sc_result result; + sc_addr connector_addr = SC_ADDR_EMPTY; + + if (sc_type_is_not_connector(type)) + { + return SC_ADDR_EMPTY; + } + + if (beg_addr == null_ptr || end_addr == null_ptr) + { + return SC_ADDR_EMPTY; + } + + if (SC_ADDR_IS_EMPTY(*beg_addr) || SC_ADDR_IS_EMPTY(*end_addr)) + { + return SC_ADDR_EMPTY; + } + + sc_element_data new_beg_ver_val; + sc_element_data new_end_ver_val; + + sc_element_data * new_beg_ver = &new_beg_ver_val; + sc_element_data * new_end_ver = &new_end_ver_val; + + sc_element * arc_el = sc_storage_allocate_new_element(txn->ctx, &connector_addr); + if (arc_el == null_ptr) + { + return SC_ADDR_EMPTY; + } + + arc_el->flags.type = type; + arc_el->arc.begin = *beg_addr; + arc_el->arc.end = *end_addr; + + sc_bool const is_edge = sc_type_has_subtype(type, sc_type_common_edge); + sc_bool const is_not_loop = SC_ADDR_IS_NOT_EQUAL(*beg_addr, *end_addr); + + // try to lock begin and end elements + sc_monitor * beg_monitor = + sc_monitor_table_get_monitor_for_addr(sc_memory_transaction_manager_get()->monitor_table, *beg_addr); + sc_monitor * end_monitor = + sc_monitor_table_get_monitor_for_addr(sc_memory_transaction_manager_get()->monitor_table, *end_addr); + sc_monitor_acquire_write_n(2, beg_monitor, end_monitor); + + if (beg_monitor == null_ptr) + return SC_ADDR_EMPTY; + if (end_monitor == null_ptr) + return SC_ADDR_EMPTY; + + result = sc_storage_get_element_data_by_addr(*beg_addr, new_beg_ver); + if (result != SC_RESULT_OK) + goto error; + + result = sc_storage_get_element_data_by_addr(*end_addr, new_end_ver); + if (result != SC_RESULT_OK) + goto error; + + // lock arcs to change output/input list + sc_storage_make_elements_incident_to_arc( + connector_addr, + arc_el, + *beg_addr, + (sc_element *)new_beg_ver, + *end_addr, + (sc_element *)new_end_ver, + SC_FALSE, + !is_not_loop); + if (is_edge && is_not_loop) + sc_storage_make_elements_incident_to_arc( + connector_addr, + arc_el, + *end_addr, + (sc_element *)new_end_ver, + *beg_addr, + (sc_element *)new_beg_ver, + SC_TRUE, + SC_FALSE); + +#ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES + if (sc_type_is_structure_and_arc(new_beg_ver->flags.type, type)) + sc_storage_update_structure_arcs(connector_addr, arc_el, *beg_addr, *end_addr, (sc_element *)new_end_ver); +#endif + + sc_transaction_element_new(txn, &connector_addr); + sc_transaction_element_change(txn, beg_addr, new_beg_ver); + sc_transaction_element_change(txn, end_addr, new_end_ver); + + sc_monitor_release_write_n(2, beg_monitor, end_monitor); + + return connector_addr; + +error: + sc_storage_free_element(connector_addr); + sc_monitor_release_write_n(2, beg_monitor, end_monitor); + return connector_addr; +} + +sc_addr sc_memory_transaction_element_free(sc_transaction const * txn, sc_addr const addr) +{ + if (txn == null_ptr) + return addr; + + sc_transaction_element_remove(txn, &addr); + return addr; +} \ No newline at end of file diff --git a/sc-memory/sc-core/src/sc-store/sc-transaction/sc_memory_transaction_operations.h b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_memory_transaction_operations.h new file mode 100644 index 000000000..0aec831b8 --- /dev/null +++ b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_memory_transaction_operations.h @@ -0,0 +1,15 @@ +#ifndef SC_MEMORY_TRANSACTION_OPERATIONS_H +#define SC_MEMORY_TRANSACTION_OPERATIONS_H + +#include "sc_transaction.h" + +sc_addr sc_memory_transaction_node_new(sc_transaction const * txn, sc_type type); +sc_addr sc_memory_transaction_link_new(sc_transaction const * txn); +sc_addr sc_memory_transaction_arc_new( + sc_transaction const * txn, + sc_type type, + sc_addr * beg_addr, + sc_addr * end_addr); +sc_addr sc_memory_transaction_element_free(sc_transaction const * txn, sc_addr addr); + +#endif // SC_MEMORY_TRANSACTION_OPERATIONS_H diff --git a/sc-memory/sc-core/src/sc-store/sc-transaction/sc_transaction.c b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_transaction.c new file mode 100644 index 000000000..6f3b18c0d --- /dev/null +++ b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_transaction.c @@ -0,0 +1,253 @@ +#include "sc_transaction.h" + +#include "sc_memory_transaction_manager.h" +#include "sc-core/sc_memory.h" +#include "sc-store/sc_element.h" +#include "sc-store/sc_storage_private.h" +#include "sc-store/sc_version_segment.h" +#include "sc-store/sc-container/sc_pair.h" + +#include + +sc_transaction * sc_transaction_new(sc_uint64 const txn_id, sc_memory_context * ctx) +{ + sc_transaction * txn = _sc_mem_new(sizeof(sc_transaction)); + if (txn == null_ptr) + return null_ptr; + + if (ctx == null_ptr) + { + sc_mem_free(txn); + return null_ptr; + } + + txn->transaction_id = txn_id; + txn->is_committed = SC_FALSE; + txn->ctx = ctx; + txn->state = SC_TRANSACTION_PENDING; + + txn->monitor = _sc_mem_new(sizeof(sc_monitor)); + if (txn->monitor == null_ptr) + { + sc_mem_free(txn); + return null_ptr; + } + sc_monitor_init(txn->monitor); + + txn->transaction_buffer = _sc_mem_new(sizeof(sc_transaction_buffer)); + if (txn->transaction_buffer == null_ptr) + { + sc_monitor_destroy(txn->monitor); + sc_mem_free(txn->monitor); + sc_mem_free(txn); + return null_ptr; + } + sc_transaction_buffer_initialize(txn->transaction_buffer, txn_id); + + txn->elements = + sc_hash_table_init(sc_hash_table_default_hash_func, sc_hash_table_default_equal_func, null_ptr, null_ptr); + if (txn->elements == null_ptr) + { + sc_transaction_buffer_destroy(txn->transaction_buffer); + sc_mem_free(txn->transaction_buffer); + sc_monitor_destroy(txn->monitor); + sc_mem_free(txn->monitor); + sc_mem_free(txn); + return null_ptr; + } + + return txn; +} + +void sc_transaction_destroy(sc_transaction * txn) +{ + if (txn != null_ptr) + { + if (txn->transaction_buffer != null_ptr) + { + sc_transaction_buffer_destroy(txn->transaction_buffer); + sc_mem_free(txn->transaction_buffer); + } + if (txn->elements != null_ptr) + { + sc_hash_table_destroy(txn->elements); + } + if (txn->monitor != null_ptr) + { + sc_monitor_destroy(txn->monitor); + sc_mem_free(txn->monitor); + } + sc_mem_free(txn); + } +} + +sc_bool _sc_transaction_validate_data(sc_element_data const * data1, sc_element_data const * data2) +{ + if (data1 == null_ptr || data2 == null_ptr) + return SC_FALSE; + + if (data1->flags.states != data2->flags.states || data1->first_out_arc.seg != data2->first_out_arc.seg + || data1->first_out_arc.offset != data2->first_out_arc.offset + || data1->first_in_arc.seg != data2->first_in_arc.seg || data1->first_in_arc.offset != data2->first_in_arc.offset + || +#ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES + data1->first_in_arc_from_structure.seg != data2->first_in_arc_from_structure.seg + || data1->first_in_arc_from_structure.offset != data2->first_in_arc_from_structure.offset || +#endif + data1->arc.begin.seg != data2->arc.begin.seg || data1->arc.begin.offset != data2->arc.begin.offset + || data1->arc.end.seg != data2->arc.end.seg || data1->arc.end.offset != data2->arc.end.offset + || data1->arc.next_begin_out_arc.seg != data2->arc.next_begin_out_arc.seg + || data1->arc.next_begin_out_arc.offset != data2->arc.next_begin_out_arc.offset + || data1->arc.prev_begin_out_arc.seg != data2->arc.prev_begin_out_arc.seg + || data1->arc.prev_begin_out_arc.offset != data2->arc.prev_begin_out_arc.offset + || data1->arc.next_begin_in_arc.seg != data2->arc.next_begin_in_arc.seg + || data1->arc.next_begin_in_arc.offset != data2->arc.next_begin_in_arc.offset + || data1->arc.next_end_out_arc.seg != data2->arc.next_end_out_arc.seg + || data1->arc.next_end_out_arc.offset != data2->arc.next_end_out_arc.offset + || data1->arc.next_end_in_arc.seg != data2->arc.next_end_in_arc.seg + || data1->arc.next_end_in_arc.offset != data2->arc.next_end_in_arc.offset + || data1->arc.prev_end_in_arc.seg != data2->arc.prev_end_in_arc.seg + || data1->arc.prev_end_in_arc.offset != data2->arc.prev_end_in_arc.offset || +#ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES + data1->arc.prev_in_arc_from_structure.seg != data2->arc.prev_in_arc_from_structure.seg + || data1->arc.prev_in_arc_from_structure.offset != data2->arc.prev_in_arc_from_structure.offset + || data1->arc.next_in_arc_from_structure.seg != data2->arc.next_in_arc_from_structure.seg + || data1->arc.next_in_arc_from_structure.offset != data2->arc.next_in_arc_from_structure.offset || +#endif + data1->incoming_arcs_count != data2->incoming_arcs_count + || data1->outgoing_arcs_count != data2->outgoing_arcs_count) + { + return SC_FALSE; + } + + return SC_TRUE; +} + +sc_bool _sc_transaction_validate_modify_elements(sc_list const * elements_list) +{ + sc_iterator * it = sc_list_iterator(elements_list); + while (sc_iterator_next(it)) + { + sc_pair const * pair = sc_iterator_get(it); + + sc_addr element_addr; + SC_ADDR_LOCAL_FROM_INT((uintptr_t)pair->first, element_addr); + sc_element_data const * snapshot = pair->second; + + sc_element * element; + sc_storage_get_element_by_addr(element_addr, &element); + + sc_element_data * current_data = sc_element_data_new(); + sc_storage_get_element_data_by_addr(element_addr, current_data); + + if (_sc_transaction_validate_data(snapshot, current_data)) + { + return SC_FALSE; + } + } + + return SC_TRUE; +} + +sc_bool sc_transaction_validate(sc_transaction * txn) +{ + if (sc_hash_table_size(txn->elements) == 0) + return SC_FALSE; + if (!_sc_transaction_validate_modify_elements(txn->transaction_buffer->modified_elements)) + return SC_FALSE; + + return SC_TRUE; +} + +void _sc_transaction_apply_modified_elements(sc_list const * elements_list, sc_uint64 const txn_id) +{ + sc_iterator * it = sc_list_iterator(elements_list); + while (sc_iterator_next(it)) + { + sc_pair const * pair = sc_iterator_get(it); + + sc_addr element_addr; + SC_ADDR_LOCAL_FROM_INT((uintptr_t)pair->first, element_addr); + + sc_element * element = null_ptr; + sc_storage_get_element_by_addr(element_addr, &element); + + sc_element_data const * new_data = pair->second; + + sc_element_version * new_version = sc_element_create_new_version(element, new_data, txn_id); + + sc_version_segment_add(element->version_history, new_version); + } +} + +void _sc_transaction_apply_deleted_elements(sc_list const * elements_list, sc_memory_context * ctx) +{ + sc_iterator * it = sc_list_iterator(elements_list); + while (sc_iterator_next(it)) + { + sc_addr element_addr; + SC_ADDR_LOCAL_FROM_INT((uintptr_t)sc_iterator_get(it), element_addr); + + sc_memory_element_free(ctx, element_addr); + } +} + +void sc_transaction_apply(sc_transaction const * txn) +{ + _sc_transaction_apply_modified_elements(txn->transaction_buffer->modified_elements, txn->transaction_id); + _sc_transaction_apply_deleted_elements(txn->transaction_buffer->deleted_elements, txn->ctx); +} + +void sc_transaction_clear(sc_transaction * txn) {} + +sc_bool sc_transaction_element_new(sc_transaction const * txn, sc_addr const * addr) +{ + if (txn == null_ptr || addr == null_ptr || txn->transaction_buffer == null_ptr) + return SC_FALSE; + + sc_hash_table_insert(txn->elements, (void *)addr, null_ptr); + + return sc_transaction_buffer_created_add(txn->transaction_buffer, addr); +} + +sc_bool sc_transaction_element_change( + sc_transaction const * txn, + sc_addr const * addr, + sc_element_data const * new_data) +{ + if (txn == null_ptr || addr == null_ptr || txn->transaction_buffer == null_ptr) + return SC_FALSE; + + if (SC_ADDR_IS_EMPTY(*addr)) + return SC_FALSE; + + sc_hash_table_insert(txn->elements, (void *)addr, null_ptr); + + return sc_transaction_buffer_modified_add(txn->transaction_buffer, addr, new_data); +} + +sc_bool sc_transaction_element_remove(sc_transaction const * txn, sc_addr const * addr) +{ + if (txn == null_ptr || addr == null_ptr || txn->transaction_buffer == null_ptr) + return SC_FALSE; + + if (SC_ADDR_IS_EMPTY(*addr)) + return SC_FALSE; + + sc_hash_table_insert(txn->elements, (void *)addr, null_ptr); + + return sc_transaction_buffer_removed_add(txn->transaction_buffer, addr); +} + +sc_bool sc_transaction_element_content_set(sc_transaction const * txn, sc_addr const * addr, sc_stream const * content) +{ + if (txn == null_ptr || addr == null_ptr || content == null_ptr || txn->transaction_buffer == null_ptr) + return SC_FALSE; + + if (SC_ADDR_IS_EMPTY(*addr)) + return SC_FALSE; + + sc_hash_table_insert(txn->elements, (void *)addr, null_ptr); + + return sc_transaction_buffer_content_set(txn->transaction_buffer, addr, content); +} \ No newline at end of file diff --git a/sc-memory/sc-core/src/sc-store/sc-transaction/sc_transaction.h b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_transaction.h new file mode 100644 index 000000000..2b1c23f3d --- /dev/null +++ b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_transaction.h @@ -0,0 +1,51 @@ +#ifndef SC_TRANSACTION_H +#define SC_TRANSACTION_H + +#include "sc-store/sc-container/sc_hash_table.h" +#include +#include +#include + +typedef enum _sc_transaction_state +{ + SC_TRANSACTION_PENDING, + SC_TRANSACTION_EXECUTED, + SC_TRANSACTION_FAILED +} sc_transaction_state; + +typedef struct sc_transaction +{ + sc_uint64 transaction_id; + sc_bool is_committed; + sc_transaction_state state; + sc_hash_table * elements; + sc_monitor * monitor; + sc_transaction_buffer * transaction_buffer; + sc_memory_context * ctx; +} sc_transaction; + +sc_transaction * sc_transaction_new(sc_uint64 txn_id, sc_memory_context * ctx); +// create a new transaction +void sc_transaction_destroy(sc_transaction * txn); +// destroy the given transaction + +sc_bool sc_transaction_element_new(sc_transaction const * txn, sc_addr const * addr); +// Writes information about adding an item to the buffer +sc_bool sc_transaction_element_change( + sc_transaction const * txn, + sc_addr const * addr, + sc_element_data const * new_data); +// Writes information about the element change to the buffer +sc_bool sc_transaction_element_remove(sc_transaction const * txn, sc_addr const * addr); +// Writes the deletion information to the buffer +sc_bool sc_transaction_element_content_set(sc_transaction const * txn, sc_addr const * addr, sc_stream const * content); +// Writes information about content changes to the buffer + +sc_bool sc_transaction_validate(sc_transaction * txn); +// check if the transaction can be applied based on element versions and free segments +void sc_transaction_apply(sc_transaction const * txn); +// apply all operations (merged versions, allocated spaces) to sc-memory +void sc_transaction_clear(sc_transaction * txn); +// deletes all transaction items and clears them without performing a commit + +#endif diff --git a/sc-memory/sc-core/src/sc-store/sc-transaction/sc_transaction_buffer.c b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_transaction_buffer.c new file mode 100644 index 000000000..b7f450e77 --- /dev/null +++ b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_transaction_buffer.c @@ -0,0 +1,232 @@ +#include "sc_transaction_buffer.h" + +#include "sc-store/sc_storage_private.h" +#include "sc-store/sc-base/sc_monitor_table.h" +#include "sc-store/sc-container/sc_pair.h" + +#include +#include +#include +#include + +typedef struct sc_content_version +{ + sc_addr addr; + sc_stream * content; +} sc_content_version; + +void sc_transaction_buffer_initialize(sc_transaction_buffer * transaction_buffer, sc_uint64 txn_id) +{ + if (transaction_buffer == null_ptr) + return; + + transaction_buffer->transaction_id = txn_id; + + if (!sc_list_init(&transaction_buffer->new_elements)) + goto error_nothing; + + if (!sc_list_init(&transaction_buffer->modified_elements)) + goto error_new_elements; + + if (!sc_list_init(&transaction_buffer->deleted_elements)) + goto error_modified_elements; + + if (!sc_list_init(&transaction_buffer->content_changes)) + goto error_deleted_elements; + + return; + +error_deleted_elements: + sc_list_destroy(transaction_buffer->deleted_elements); +error_modified_elements: + sc_list_destroy(transaction_buffer->modified_elements); +error_new_elements: + sc_list_destroy(transaction_buffer->new_elements); +error_nothing: + return; +} + +void sc_transaction_buffer_destroy(sc_transaction_buffer * transaction_buffer) +{ + if (transaction_buffer == null_ptr) + return; + + if (transaction_buffer->new_elements != null_ptr) + { + sc_list_destroy(transaction_buffer->new_elements); + transaction_buffer->new_elements = null_ptr; + } + + if (transaction_buffer->modified_elements != null_ptr) + { + sc_list_destroy(transaction_buffer->modified_elements); + transaction_buffer->modified_elements = null_ptr; + } + + if (transaction_buffer->deleted_elements != null_ptr) + { + sc_list_destroy(transaction_buffer->deleted_elements); + transaction_buffer->deleted_elements = null_ptr; + } + + if (transaction_buffer->content_changes != null_ptr) + { + sc_iterator * it = sc_list_iterator(transaction_buffer->content_changes); + while (sc_iterator_next(it)) + { + sc_pair * pair = sc_iterator_get(it); + if (pair == null_ptr) + continue; + + if (pair->second != null_ptr) + { + sc_stream_free(pair->second); + pair->second = null_ptr; + } + + sc_mem_free(pair); + } + sc_iterator_destroy(it); + + sc_list_destroy(transaction_buffer->content_changes); + transaction_buffer->content_changes = null_ptr; + } +} + +sc_bool sc_transaction_buffer_created_add(sc_transaction_buffer const * buffer, sc_addr const * addr) +{ + if (buffer == null_ptr || buffer->new_elements == null_ptr) + return SC_FALSE; + + if (SC_ADDR_IS_EMPTY(*addr)) + return SC_FALSE; + + sc_uint32 const addr_hash = SC_ADDR_LOCAL_TO_INT(*addr); + if (sc_transaction_buffer_contains_created(buffer, addr)) + return SC_TRUE; + + if (sc_list_push_back(buffer->new_elements, (void *)(uintptr_t)addr_hash) == null_ptr) + return SC_FALSE; + + return SC_TRUE; +} + +sc_bool sc_transaction_buffer_modified_add( + sc_transaction_buffer const * buffer, + sc_addr const * addr, + sc_element_data const * new_element_data) +{ + if (buffer == null_ptr || buffer->modified_elements == null_ptr) + return SC_FALSE; + + if (SC_ADDR_IS_EMPTY(*addr)) + return SC_FALSE; + + sc_element * element = null_ptr; + if (sc_storage_get_element_by_addr(*addr, &element) != SC_RESULT_OK || element == null_ptr) + return SC_FALSE; + + sc_element_data * snapshot = sc_mem_new(sc_element_data, 1); + if (snapshot == null_ptr) + return SC_FALSE; + *snapshot = *new_element_data; + + sc_pair * pair = sc_make_pair((void *)(uintptr_t)SC_ADDR_LOCAL_TO_INT(*addr), snapshot); + if (pair == null_ptr) + { + sc_mem_free(snapshot); + return SC_FALSE; + } + + if (sc_list_push_back(buffer->modified_elements, pair) == null_ptr) + { + sc_mem_free(snapshot); + sc_mem_free(pair); + return SC_FALSE; + } + + return SC_TRUE; +} + +sc_bool sc_transaction_buffer_removed_add(sc_transaction_buffer const * buffer, sc_addr const * addr) +{ + if (buffer == null_ptr || buffer->deleted_elements == null_ptr) + return SC_FALSE; + + if (SC_ADDR_IS_EMPTY(*addr)) + return SC_FALSE; + + sc_uint32 const addr_hash = SC_ADDR_LOCAL_TO_INT(*addr); + + sc_iterator * it = sc_list_iterator(buffer->deleted_elements); + while (sc_iterator_next(it)) + { + sc_uint32 const stored_hash = (uintptr_t)sc_iterator_get(it); + if (stored_hash == addr_hash) + { + sc_iterator_destroy(it); + return SC_TRUE; + } + } + sc_iterator_destroy(it); + + if (sc_list_push_back(buffer->deleted_elements, (void *)(uintptr_t)addr_hash) == null_ptr) + return SC_FALSE; + + return SC_TRUE; +} + +sc_bool sc_transaction_buffer_content_set( + sc_transaction_buffer const * buffer, + sc_addr const * addr, + sc_stream const * content) +{ + if (buffer == null_ptr || buffer->content_changes == null_ptr) + return SC_FALSE; + + if (SC_ADDR_IS_EMPTY(*addr)) + return SC_FALSE; + + sc_uint32 const addr_hash = SC_ADDR_LOCAL_TO_INT(*addr); + + sc_iterator * it = sc_list_iterator(buffer->content_changes); + while (sc_iterator_next(it)) + { + sc_pair * pair = sc_iterator_get(it); + sc_uint32 const stored_hash = (uintptr_t)pair->first; + if (stored_hash == addr_hash) + { + pair->second = (void *)content; + sc_iterator_destroy(it); + return SC_TRUE; + } + } + sc_iterator_destroy(it); + + sc_pair const * content_pair = sc_make_pair((void *)(uintptr_t)addr_hash, (void *)content); + if (sc_list_push_back(buffer->content_changes, (void *)content_pair) == null_ptr) + return SC_FALSE; + + return SC_TRUE; +} + +sc_bool sc_transaction_buffer_contains_created(sc_transaction_buffer const * buffer, sc_addr const * addr) +{ + if (buffer == null_ptr || buffer->new_elements == null_ptr) + return SC_FALSE; + + sc_uint32 const addr_hash = SC_ADDR_LOCAL_TO_INT(*addr); + sc_iterator * it = sc_list_iterator(buffer->new_elements); + while (sc_iterator_next(it)) + { + sc_uint32 const stored_hash = (uintptr_t)sc_iterator_get(it); + if (stored_hash == addr_hash) + { + sc_iterator_destroy(it); + return SC_TRUE; + } + } + sc_iterator_destroy(it); + + return SC_FALSE; +} diff --git a/sc-memory/sc-core/src/sc-store/sc-transaction/sc_transaction_buffer.h b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_transaction_buffer.h new file mode 100644 index 000000000..2d0ffc035 --- /dev/null +++ b/sc-memory/sc-core/src/sc-store/sc-transaction/sc_transaction_buffer.h @@ -0,0 +1,34 @@ +#ifndef SC_TRANSACTION_BUFFER_H +#define SC_TRANSACTION_BUFFER_H + +#include +#include +#include +#include + +typedef struct sc_transaction_buffer +{ + sc_list * new_elements; + sc_list * modified_elements; + sc_list * deleted_elements; + sc_list * content_changes; + sc_uint64 transaction_id; +} sc_transaction_buffer; + +void sc_transaction_buffer_initialize(sc_transaction_buffer * transaction_buffer, sc_uint64 txn_id); +void sc_transaction_buffer_destroy(sc_transaction_buffer * transaction_buffer); + +sc_bool sc_transaction_buffer_created_add(sc_transaction_buffer const * buffer, sc_addr const * addr); +sc_bool sc_transaction_buffer_modified_add( + sc_transaction_buffer const * buffer, + sc_addr const * addr, + sc_element_data const * element_data); +sc_bool sc_transaction_buffer_removed_add(sc_transaction_buffer const * buffer, sc_addr const * addr); +sc_bool sc_transaction_buffer_content_set( + sc_transaction_buffer const * buffer, + sc_addr const * addr, + sc_stream const * content); + +sc_bool sc_transaction_buffer_contains_created(sc_transaction_buffer const * buffer, sc_addr const * addr); + +#endif diff --git a/sc-memory/sc-core/src/sc-store/sc_element.h b/sc-memory/sc-core/src/sc-store/sc_element.h index de0c9f640..911cbdb62 100644 --- a/sc-memory/sc-core/src/sc-store/sc_element.h +++ b/sc-memory/sc-core/src/sc-store/sc_element.h @@ -51,6 +51,9 @@ struct _sc_element_flags sc_states states; }; +struct sc_version_segment; +typedef struct sc_version_segment sc_version_segment; + struct _sc_element { sc_element_flags flags; @@ -65,6 +68,8 @@ struct _sc_element sc_uint32 incoming_arcs_count; sc_uint32 outgoing_arcs_count; + + sc_version_segment * version_history; }; #endif diff --git a/sc-memory/sc-core/src/sc-store/sc_storage.c b/sc-memory/sc-core/src/sc-store/sc_storage.c index 36c7594bd..df2f42c51 100644 --- a/sc-memory/sc-core/src/sc-store/sc_storage.c +++ b/sc-memory/sc-core/src/sc-store/sc_storage.c @@ -21,6 +21,9 @@ #include "sc_storage_private.h" #include "sc_memory_private.h" +#include "sc_version_segment.h" + +#include "sc-transaction/sc_element_version.h" sc_storage * storage = null_ptr; @@ -173,6 +176,26 @@ sc_result sc_storage_get_element_by_addr(sc_addr addr, sc_element ** el) return result; } +sc_result sc_storage_get_element_data_by_addr(sc_addr const addr, sc_element_data * el_data) +{ + sc_element * el = null_ptr; + + if (sc_storage_get_element_by_addr(addr, &el) != SC_RESULT_OK || el == null_ptr) + return SC_RESULT_ERROR; + + el_data->flags = el->flags; + el_data->first_out_arc = el->first_out_arc; + el_data->first_in_arc = el->first_in_arc; +#ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES + el_data->first_in_arc_from_structure = el->first_in_arc_from_structure; +#endif + el_data->arc = el->arc; + el_data->incoming_arcs_count = el->incoming_arcs_count; + el_data->outgoing_arcs_count = el->outgoing_arcs_count; + + return SC_RESULT_OK; +} + sc_result sc_storage_free_element(sc_addr addr) { sc_result result = SC_RESULT_ERROR_ADDR_IS_NOT_VALID; @@ -411,7 +434,11 @@ sc_element * sc_storage_allocate_new_element(sc_memory_context const * ctx, sc_a } if (element != null_ptr) + { element->flags.states |= SC_STATE_ELEMENT_EXIST; + // Add param to select usage of version history? + element->version_history = sc_version_segment_new(); + } return element; } @@ -920,7 +947,7 @@ sc_addr sc_storage_link_new_ext(sc_memory_context const * ctx, sc_type type, sc_ return addr; } -void _sc_storage_make_elements_incident_to_arc( +void sc_storage_make_elements_incident_to_arc( sc_addr connector_addr, sc_element * arc_el, sc_addr beg_addr, @@ -990,7 +1017,7 @@ void _sc_storage_make_elements_incident_to_arc( } #ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES -void _sc_storage_update_structure_arcs( +void sc_storage_update_structure_arcs( sc_addr connector_addr, sc_element * arc_el, sc_addr beg_addr, @@ -1079,15 +1106,15 @@ sc_addr sc_storage_arc_new_ext( goto error; // lock arcs to change output/input list - _sc_storage_make_elements_incident_to_arc( + sc_storage_make_elements_incident_to_arc( connector_addr, arc_el, beg_addr, beg_el, end_addr, end_el, SC_FALSE, !is_not_loop); if (is_edge && is_not_loop) - _sc_storage_make_elements_incident_to_arc( + sc_storage_make_elements_incident_to_arc( connector_addr, arc_el, end_addr, end_el, beg_addr, beg_el, SC_TRUE, SC_FALSE); #ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES if (sc_type_is_structure_and_arc(beg_el->flags.type, type)) - _sc_storage_update_structure_arcs(connector_addr, arc_el, beg_addr, end_addr, end_el); + sc_storage_update_structure_arcs(connector_addr, arc_el, beg_addr, end_addr, end_el); #endif // emit events diff --git a/sc-memory/sc-core/src/sc-store/sc_storage_private.h b/sc-memory/sc-core/src/sc-store/sc_storage_private.h index d0957a05c..498367917 100644 --- a/sc-memory/sc-core/src/sc-store/sc_storage_private.h +++ b/sc-memory/sc-core/src/sc-store/sc_storage_private.h @@ -14,6 +14,7 @@ #include "sc-store/sc_storage_dump_manager.h" #include "sc-store/sc-base/sc_monitor_table_private.h" +#include "sc-transaction/sc_element_version.h" struct _sc_storage { @@ -41,6 +42,25 @@ sc_element * sc_storage_allocate_new_element(sc_memory_context const * ctx, sc_a sc_result sc_storage_get_element_by_addr(sc_addr addr, sc_element ** el); +sc_result sc_storage_get_element_data_by_addr(sc_addr addr, sc_element_data * el_data); + sc_result sc_storage_free_element(sc_addr addr); +void sc_storage_make_elements_incident_to_arc( + sc_addr connector_addr, + sc_element * arc_el, + sc_addr beg_addr, + sc_element * beg_el, + sc_addr end_addr, + sc_element * end_el, + sc_bool is_reverse, + sc_bool is_loop); + +void sc_storage_update_structure_arcs( + sc_addr connector_addr, + sc_element * arc_el, + sc_addr beg_addr, + sc_addr end_addr, + sc_element * end_el); + #endif diff --git a/sc-memory/sc-core/src/sc-store/sc_version_segment.c b/sc-memory/sc-core/src/sc-store/sc_version_segment.c new file mode 100644 index 000000000..db548b44d --- /dev/null +++ b/sc-memory/sc-core/src/sc-store/sc_version_segment.c @@ -0,0 +1,59 @@ +#include "sc_version_segment.h" +#include +#include +#include + +sc_version_segment * sc_version_segment_new(void) +{ + sc_version_segment * segment = sc_mem_new(sc_version_segment, 1); + if (!segment) + return null_ptr; + sc_mem_set(segment, 0, sizeof(*segment)); + segment->monitor = _sc_mem_new(sizeof(sc_monitor)); + if (segment->monitor == null_ptr) + { + sc_mem_free(segment); + return null_ptr; + } + sc_monitor_init(segment->monitor); + return segment; +} + +void sc_version_segment_free(sc_version_segment * segment) +{ + if (!segment) + return; + sc_monitor_destroy(segment->monitor); + sc_mem_free(segment); +} + +sc_uint64 sc_version_segment_get_next_version_id(sc_element const * element) +{ + sc_version_segment const * segment = element->version_history; + sc_monitor * monitor = segment->monitor; + + sc_monitor_acquire_read(monitor); + sc_uint64 const next_version_id = segment->count; + sc_monitor_release_read(monitor); + + return next_version_id; +} + +void sc_version_segment_add(sc_version_segment * segment, sc_element_version * new_version) +{ + if (segment == null_ptr || new_version == null_ptr) + return; + + sc_monitor_acquire_write(segment->monitor); + + if (segment->count >= SC_VERSION_SEGMENT_SIZE) + { + sc_monitor_release_write(segment->monitor); + return; + } + + segment->versions[segment->count++] = *new_version->data; + segment->current_version = new_version; + + sc_monitor_release_write(segment->monitor); +} diff --git a/sc-memory/sc-core/src/sc-store/sc_version_segment.h b/sc-memory/sc-core/src/sc-store/sc_version_segment.h new file mode 100644 index 000000000..253f8cfd9 --- /dev/null +++ b/sc-memory/sc-core/src/sc-store/sc_version_segment.h @@ -0,0 +1,27 @@ +#ifndef SC_VERSION_SEGMENT_H +#define SC_VERSION_SEGMENT_H + +#include "sc-base/sc_monitor_private.h" +#include "sc-transaction/sc_element_version.h" + +#include + +#define SC_VERSION_SEGMENT_SIZE 512 + +typedef struct sc_version_segment +{ + sc_element_data versions[SC_VERSION_SEGMENT_SIZE]; + sc_addr_offset count; + sc_monitor * monitor; + sc_element_version * current_version; +} sc_version_segment; + +sc_version_segment * sc_version_segment_new(void); + +void sc_version_segment_free(sc_version_segment * segment); + +sc_uint64 sc_version_segment_get_next_version_id(sc_element const * element); + +void sc_version_segment_add(sc_version_segment * segment, sc_element_version * new_version); + +#endif // SC_VERSION_SEGMENT_H diff --git a/sc-memory/sc-core/src/sc_memory.c b/sc-memory/sc-core/src/sc_memory.c index 0d5ad1b07..df9684e73 100644 --- a/sc-memory/sc-core/src/sc_memory.c +++ b/sc-memory/sc-core/src/sc_memory.c @@ -20,11 +20,13 @@ #include "sc-core/sc_types.h" #include "sc-core/sc-base/sc_allocator.h" #include "sc-core/sc-container/sc_string.h" +#include "sc-store/sc-transaction/sc_memory_transaction_manager.h" struct _sc_memory { sc_addr myself_addr; sc_memory_context_manager * context_manager; + sc_memory_transaction_manager * transaction_manager; }; sc_memory * memory = null_ptr; @@ -50,6 +52,14 @@ sc_memory_context * sc_memory_initialize(sc_memory_params const * params, sc_mem goto error; } + sc_memory_transaction_manager * transaction_manager = sc_mem_new(sc_memory_transaction_manager, 1); + if (sc_memory_transaction_manager_initialize(transaction_manager) != SC_RESULT_OK) + { + s_memory_default_ctx = null_ptr; + sc_memory_error("Error while initialize sc-transaction manager"); + goto error; + } + memory = sc_mem_new(sc_memory, 1); sc_storage_start_new_process(); diff --git a/sc-memory/sc-core/tests/CMakeLists.txt b/sc-memory/sc-core/tests/CMakeLists.txt index 103c64e89..0319b7972 100644 --- a/sc-memory/sc-core/tests/CMakeLists.txt +++ b/sc-memory/sc-core/tests/CMakeLists.txt @@ -29,3 +29,23 @@ make_tests_from_folder(${CMAKE_CURRENT_SOURCE_DIR}/units/fs-storage if(${SC_CLANG_FORMAT_CODE}) target_clangformat_setup(sc-core-fs-storage-tests) endif() + +make_tests_from_folder(${CMAKE_CURRENT_SOURCE_DIR}/units/transaction + NAME sc-core-transaction-tests + DEPENDS ${glib_LIBRARIES} sc-memory + INCLUDES ${glib_INCLUDE_DIRS} ${SC_CORE_SRC} +) + +if(${SC_CLANG_FORMAT_CODE}) + target_clangformat_setup(sc-core-transaction-tests) +endif() + +make_tests_from_folder(${CMAKE_CURRENT_SOURCE_DIR}/benchmark + NAME sc-core-transaction-benchmark-tests + DEPENDS ${glib_LIBRARIES} sc-memory + INCLUDES ${glib_INCLUDE_DIRS} ${SC_CORE_SRC} +) + +if(${SC_CLANG_FORMAT_CODE}) + target_clangformat_setup(sc-core-transaction-benchmark-tests) +endif() \ No newline at end of file diff --git a/sc-memory/sc-core/tests/benchmark/CMakeLists.txt b/sc-memory/sc-core/tests/benchmark/CMakeLists.txt new file mode 100644 index 000000000..307c40d4b --- /dev/null +++ b/sc-memory/sc-core/tests/benchmark/CMakeLists.txt @@ -0,0 +1,40 @@ +cmake_minimum_required(VERSION 3.10) +project(sc-memory-benchmarks) + +# Находим необходимые зависимости +find_package(benchmark REQUIRED) +find_package(ScMemory REQUIRED) + +# Собираем все исходники бенчмарков +file(GLOB_RECURSE BENCHMARK_SOURCES + "*.cpp" + "units/*/*.hpp" +) + +# Создаем единый исполняемый файл для всех бенчмарков +add_executable(sc-memory-benchmarks ${BENCHMARK_SOURCES}) + +# Подключаем зависимости +target_link_libraries(sc-memory-benchmarks + PRIVATE + ScMemory::sc-memory + benchmark::benchmark +) + +# Добавляем тест в CTest +add_test( + NAME sc-memory-benchmarks + COMMAND sc-memory-benchmarks +) + +# Настройка clang-format если нужно +if(SC_CLANG_FORMAT_CODE) + target_clangformat_setup(sc-memory-benchmarks) +endif() + +# Указываем пути для включения заголовочных файлов +target_include_directories(sc-memory-benchmarks + PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR} + ${benchmark_INCLUDE_DIRS} +) \ No newline at end of file diff --git a/sc-memory/sc-core/tests/benchmark/main.cpp b/sc-memory/sc-core/tests/benchmark/main.cpp new file mode 100644 index 000000000..4201e0d90 --- /dev/null +++ b/sc-memory/sc-core/tests/benchmark/main.cpp @@ -0,0 +1,53 @@ +#include "benchmark/benchmark.h" +#include "units/transaction/test_sc_transaction_add_node.hpp" + +#include + +template +void BM_MemoryThreaded(benchmark::State & state) +{ + static std::atomic_int ctxNum = {0}; + BMType test; + if (state.thread_index() == 0) + test.Initialize(); + + auto start = std::chrono::high_resolution_clock::now(); + uint32_t iterations = 0; + for (auto t : state) + { + state.PauseTiming(); + if (!test.HasContext()) + { + test.InitContext(); + ctxNum.fetch_add(1); + } + state.ResumeTiming(); + + test.Run(); + ++iterations; + } + auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration elapsed = end - start; + std::stringstream stream; + stream << state.max_iterations << " " << elapsed.count() << std::endl; + std::cout << stream.str(); + + state.counters["rate"] = benchmark::Counter(iterations, benchmark::Counter::kIsRate); + if (state.thread_index() == 0) + { + while (ctxNum.load() != 0); + test.Shutdown(); + } + else + { + test.DestroyContext(); + ctxNum.fetch_add(-1); + } +} + +int constexpr kNodeIters = 1000000; + +BENCHMARK_TEMPLATE(BM_MemoryThreaded, TestGenerateNode) +->Threads(1) +->Iterations(kNodeIters) +->Unit(benchmark::TimeUnit::kMicrosecond); \ No newline at end of file diff --git a/sc-memory/sc-core/tests/benchmark/units/memory_test.hpp b/sc-memory/sc-core/tests/benchmark/units/memory_test.hpp new file mode 100644 index 000000000..11144807d --- /dev/null +++ b/sc-memory/sc-core/tests/benchmark/units/memory_test.hpp @@ -0,0 +1,56 @@ +/* +* This source file is part of an OSTIS project. For the latest info, see http://ostis.net +* Distributed under the MIT License +* (See accompanying file COPYING.MIT or copy at http://opensource.org/licenses/MIT) +*/ + +#pragma once + +#include +#include "sc-memory/sc_memory.hpp" +#include "sc-core/sc_memory.h" + +class TestMemory +{ +public: + void Initialize(size_t objectsNum = 0) + { + sc_memory_params params; + sc_memory_params_clear(¶ms); + params.clear = SC_TRUE; + params.storage = "test_repo"; + + ScMemory::LogMute(); + ScMemory::Initialize(params); + + InitContext(); + Setup(objectsNum); + } + + void Shutdown() + { + m_ctx.reset(); + + ScMemory::Shutdown(false); + } + + void InitContext() + { + m_ctx = std::make_unique(); + } + + void DestroyContext() + { + m_ctx.reset(); + } + + bool HasContext() const + { + return static_cast(m_ctx); + } + + virtual void Setup(size_t objectsNum) {} + +protected: + std::unique_ptr m_ctx {}; +}; \ No newline at end of file diff --git a/sc-memory/sc-core/tests/benchmark/units/transaction/test_sc_transaction_add_node.hpp b/sc-memory/sc-core/tests/benchmark/units/transaction/test_sc_transaction_add_node.hpp new file mode 100644 index 000000000..5a9c66150 --- /dev/null +++ b/sc-memory/sc-core/tests/benchmark/units/transaction/test_sc_transaction_add_node.hpp @@ -0,0 +1,12 @@ +#pragma once + +#include "../memory_test.hpp" + +class TestGenerateNode : public TestMemory +{ +public: + void Run() + { + m_ctx->GenerateNode(ScType::ConstNode); + } +}; \ No newline at end of file diff --git a/sc-memory/sc-core/tests/units/transaction/test_sc_memory_transaction_operations.cpp b/sc-memory/sc-core/tests/units/transaction/test_sc_memory_transaction_operations.cpp new file mode 100644 index 000000000..1186f1bd3 --- /dev/null +++ b/sc-memory/sc-core/tests/units/transaction/test_sc_memory_transaction_operations.cpp @@ -0,0 +1,188 @@ +#include +#include + +extern "C" +{ +#include +#include "sc-store/sc-container/sc_pair.h" +#include "sc-store/sc_storage_private.h" +#include "sc-store/sc_version_segment.h" +#include "sc-store/sc-transaction/sc_memory_transaction_manager.h" +#include +} + +class ScMemoryTransactionArcNewTest : public ScMemoryTest +{ +protected: + sc_transaction * transaction = nullptr; + sc_addr beg_addr = {}, end_addr = {}; + + void SetUp() override + { + ScMemoryTest::SetUp(); + beg_addr = sc_memory_node_new(m_ctx->GetRealContext(), sc_type_node); + end_addr = sc_memory_node_new(m_ctx->GetRealContext(), sc_type_node); + + transaction = sc_transaction_new(123, m_ctx->GetRealContext()); + ASSERT_NE(transaction, nullptr); + } + + void TearDown() override + { + if (transaction) + sc_transaction_destroy(transaction); + ScMemoryTest::TearDown(); + } +}; + +sc_result sc_transaction_buffer_find_new_element( + sc_transaction_buffer const * buffer, + sc_addr const * addr, + sc_uint32 * out_hash) +{ + if (buffer == null_ptr || buffer->new_elements == null_ptr || addr == null_ptr || out_hash == null_ptr) + return SC_RESULT_ERROR_INVALID_PARAMS; + + if (SC_ADDR_IS_EMPTY(*addr)) + return SC_RESULT_ERROR_ADDR_IS_NOT_VALID; + + sc_uint32 const addr_hash = SC_ADDR_LOCAL_TO_INT(*addr); + sc_iterator * it = sc_list_iterator(buffer->new_elements); + while (sc_iterator_next(it)) + { + sc_uint32 const stored_hash = (uintptr_t)sc_iterator_get(it); + if (stored_hash == addr_hash) + { + *out_hash = stored_hash; + sc_iterator_destroy(it); + return SC_RESULT_OK; + } + } + sc_iterator_destroy(it); + + return SC_RESULT_ERROR_NOT_FOUND; +} + +sc_result sc_transaction_buffer_find_modified_element( + sc_transaction_buffer const * buffer, + sc_addr const * addr, + sc_element_data ** out_data) +{ + if (buffer == null_ptr || buffer->modified_elements == null_ptr || addr == null_ptr || out_data == null_ptr) + return SC_RESULT_ERROR_INVALID_PARAMS; + + if (SC_ADDR_IS_EMPTY(*addr)) + return SC_RESULT_ERROR_ADDR_IS_NOT_VALID; + + sc_uint32 const addr_hash = SC_ADDR_LOCAL_TO_INT(*addr); + sc_iterator * it = sc_list_iterator(buffer->modified_elements); + while (sc_iterator_next(it)) + { + auto pair = static_cast(sc_iterator_get(it)); + + sc_uint32 stored_hash = reinterpret_cast(pair->first); + if (stored_hash == addr_hash) + { + *out_data = static_cast(pair->second); + sc_iterator_destroy(it); + return SC_RESULT_OK; + } + } + sc_iterator_destroy(it); + + return SC_RESULT_ERROR_NOT_FOUND; +} + +TEST_F(ScMemoryTransactionArcNewTest, ValidParams) +{ + EXPECT_EQ(sc_memory_transaction_arc_new(transaction, sc_type_pos_arc, &beg_addr, &end_addr), SC_RESULT_OK); +} + +TEST_F(ScMemoryTransactionArcNewTest, InvalidAddress) +{ + sc_addr empty_addr = SC_ADDR_EMPTY; + + EXPECT_EQ( + sc_memory_transaction_arc_new(transaction, sc_type_pos_arc, &empty_addr, &end_addr), + SC_RESULT_ERROR_ADDR_IS_NOT_VALID); + EXPECT_EQ( + sc_memory_transaction_arc_new(transaction, sc_type_pos_arc, &beg_addr, &empty_addr), + SC_RESULT_ERROR_ADDR_IS_NOT_VALID); +} + +TEST_F(ScMemoryTransactionArcNewTest, InvalidType) +{ + EXPECT_EQ( + sc_memory_transaction_arc_new(transaction, sc_type_node, &beg_addr, &end_addr), + SC_RESULT_ERROR_ELEMENT_IS_NOT_CONNECTOR); +} + +TEST_F(ScMemoryTransactionArcNewTest, NullParams) +{ + EXPECT_EQ( + sc_memory_transaction_arc_new(nullptr, sc_type_pos_arc, &beg_addr, &end_addr), SC_RESULT_ERROR_INVALID_PARAMS); + EXPECT_EQ( + sc_memory_transaction_arc_new(transaction, sc_type_pos_arc, nullptr, &end_addr), SC_RESULT_ERROR_INVALID_PARAMS); + EXPECT_EQ( + sc_memory_transaction_arc_new(transaction, sc_type_pos_arc, &beg_addr, nullptr), SC_RESULT_ERROR_INVALID_PARAMS); +} + +TEST_F(ScMemoryTransactionArcNewTest, BufferCheck) +{ + sc_element * beg_el_before = nullptr; + sc_storage_get_element_by_addr(beg_addr, &beg_el_before); + + sc_element * end_el_before = nullptr; + sc_storage_get_element_by_addr(end_addr, &end_el_before); + + EXPECT_EQ(sc_memory_transaction_arc_new(transaction, sc_type_pos_arc, &beg_addr, &end_addr), SC_RESULT_OK); + + sc_element * beg_el_after = nullptr; + sc_storage_get_element_by_addr(beg_addr, &beg_el_after); + + sc_element * end_el_after = nullptr; + sc_storage_get_element_by_addr(end_addr, &end_el_after); + + EXPECT_EQ(beg_el_after->outgoing_arcs_count, beg_el_after->outgoing_arcs_count); + EXPECT_EQ(beg_el_after->incoming_arcs_count, beg_el_after->incoming_arcs_count); + + sc_element_data * beg_el_ver = nullptr; + sc_transaction_buffer_find_modified_element(transaction->transaction_buffer, &beg_addr, &beg_el_ver); + + sc_element_data * end_el_ver = nullptr; + sc_transaction_buffer_find_modified_element(transaction->transaction_buffer, &end_addr, &end_el_ver); + + size_t const beg_diff = beg_el_ver->outgoing_arcs_count - beg_el_after->outgoing_arcs_count; + size_t const end_diff = end_el_ver->incoming_arcs_count - end_el_after->incoming_arcs_count; + + EXPECT_EQ(beg_diff, 1); + EXPECT_EQ(end_diff, 1); +} + +TEST_F(ScMemoryTransactionArcNewTest, FullTxnTest) +{ + EXPECT_EQ(sc_memory_transaction_arc_new(transaction, sc_type_pos_arc, &beg_addr, &end_addr), SC_RESULT_OK); + + sc_element * beg_el_before = nullptr; + sc_storage_get_element_by_addr(beg_addr, &beg_el_before); + sc_element * end_el_before = nullptr; + sc_storage_get_element_by_addr(end_addr, &end_el_before); + + EXPECT_EQ(sc_hash_table_size(transaction->elements), 3); + EXPECT_EQ(beg_el_before->version_history->count, 0); + EXPECT_EQ(end_el_before->version_history->count, 0); + + EXPECT_EQ(sc_memory_transaction_commit(transaction), SC_RESULT_OK); + + while (transaction->state != SC_TRANSACTION_EXECUTED) + { + } + + sc_element * beg_el_after = nullptr; + sc_storage_get_element_by_addr(beg_addr, &beg_el_after); + sc_element * end_el_after = nullptr; + sc_storage_get_element_by_addr(end_addr, &end_el_after); + + EXPECT_EQ(beg_el_before->version_history->count, 1); + EXPECT_EQ(end_el_before->version_history->count, 1); +} \ No newline at end of file diff --git a/sc-memory/sc-core/tests/units/transaction/test_sc_transaction.cpp b/sc-memory/sc-core/tests/units/transaction/test_sc_transaction.cpp new file mode 100644 index 000000000..b43e86ac2 --- /dev/null +++ b/sc-memory/sc-core/tests/units/transaction/test_sc_transaction.cpp @@ -0,0 +1,111 @@ +#include +#include +#include + +extern "C" +{ +#include +#include +#include +} + +class ScTransactionTest : public ScMemoryTest +{ +protected: + sc_transaction * transaction = nullptr; + sc_uint64 const test_txn_id = 123; + + void SetUp() override + { + ScMemoryTest::SetUp(); + transaction = sc_transaction_new(test_txn_id, m_ctx->GetRealContext()); + ASSERT_NE(transaction, nullptr); + } + + void TearDown() override + { + if (transaction != nullptr) + sc_transaction_destroy(transaction); + ScMemoryTest::TearDown(); + } +}; + +TEST_F(ScTransactionTest, CreationAndDestruction) +{ + EXPECT_EQ(transaction->transaction_id, test_txn_id); + EXPECT_EQ(transaction->is_committed, SC_FALSE); + EXPECT_NE(transaction->transaction_buffer, nullptr); + EXPECT_NE(transaction->elements, nullptr); +} + +TEST_F(ScTransactionTest, TransactionElementNew) +{ + constexpr sc_addr addr = {1, 2}; +} + +TEST_F(ScTransactionTest, TransactionElementRemove) +{ + constexpr sc_addr addr = {3, 4}; + + EXPECT_TRUE(sc_transaction_element_remove(transaction, &addr)); + + constexpr sc_uint32 addr_hash = SC_ADDR_LOCAL_TO_INT(addr); + sc_iterator * it = sc_list_iterator(transaction->transaction_buffer->deleted_elements); + sc_bool found = SC_FALSE; + while (sc_iterator_next(it)) + { + if (reinterpret_cast(sc_iterator_get(it)) == addr_hash) + { + found = SC_TRUE; + break; + } + } + sc_iterator_destroy(it); + EXPECT_TRUE(found); + + EXPECT_FALSE(sc_transaction_element_remove(nullptr, &addr)); + EXPECT_FALSE(sc_transaction_element_remove(transaction, nullptr)); +} + +TEST_F(ScTransactionTest, TransactionElementContentSet) +{ + constexpr sc_addr addr = {5, 6}; + + sc_stream * stream = sc_stream_memory_new("content", 7, SC_STREAM_FLAG_READ, SC_FALSE); + ASSERT_NE(stream, nullptr); + + EXPECT_TRUE(sc_transaction_element_content_set(transaction, &addr, stream)); + + constexpr sc_uint32 addr_hash = SC_ADDR_LOCAL_TO_INT(addr); + sc_iterator * it = sc_list_iterator(transaction->transaction_buffer->content_changes); + sc_bool found = SC_FALSE; + while (sc_iterator_next(it)) + { + sc_pair * pair = static_cast(sc_iterator_get(it)); + if (reinterpret_cast(pair->first) == addr_hash) + { + EXPECT_EQ(pair->second, stream); + found = SC_TRUE; + break; + } + } + sc_iterator_destroy(it); + EXPECT_TRUE(found); + + EXPECT_FALSE(sc_transaction_element_content_set(nullptr, &addr, stream)); + EXPECT_FALSE(sc_transaction_element_content_set(transaction, nullptr, stream)); + EXPECT_FALSE(sc_transaction_element_content_set(transaction, &addr, nullptr)); +} + +TEST_F(ScTransactionTest, TransactionValidation) +{ + EXPECT_FALSE(sc_transaction_validate(transaction)); +} + +TEST_F(ScTransactionTest, TransactionEdgeCases) +{ + sc_transaction_destroy(nullptr); + + sc_transaction * failed_txn = sc_transaction_new(0, m_ctx->GetRealContext()); + EXPECT_NE(failed_txn, nullptr); +} \ No newline at end of file diff --git a/sc-memory/sc-core/tests/units/transaction/test_sc_transaction_buffer.cpp b/sc-memory/sc-core/tests/units/transaction/test_sc_transaction_buffer.cpp new file mode 100644 index 000000000..7ce5f9e85 --- /dev/null +++ b/sc-memory/sc-core/tests/units/transaction/test_sc_transaction_buffer.cpp @@ -0,0 +1,142 @@ +#include +#include +#include + +extern "C" +{ +#include +#include +#include +#include +#include +#include +#include +#include +#include +} + +class ScTransactionBufferTest : public ScMemoryTest +{ +protected: + sc_transaction_buffer* buffer = sc_mem_new(sc_transaction_buffer, 1); + + void SetUp() override + { + ScMemoryTest::SetUp(); + sc_transaction_buffer_initialize(buffer, 1); + ASSERT_NE(buffer, nullptr); + } + + void TearDown() override + { + sc_transaction_buffer_destroy(buffer); + sc_mem_free(buffer); + ScMemoryTest::TearDown(); + } +}; + +TEST_F(ScTransactionBufferTest, InitializationTest) +{ + EXPECT_NE(buffer->new_elements, nullptr); + EXPECT_NE(buffer->modified_elements, nullptr); + EXPECT_NE(buffer->deleted_elements, nullptr); + EXPECT_NE(buffer->content_changes, nullptr); + EXPECT_EQ(buffer->transaction_id, 1u); +} + +TEST_F(ScTransactionBufferTest, CreatedAddTest) +{ + constexpr sc_addr addr = {1, 1}; + EXPECT_TRUE(sc_transaction_buffer_created_add(buffer, &addr)); + EXPECT_TRUE(sc_transaction_buffer_contains_created(buffer, &addr)); +} + +TEST_F(ScTransactionBufferTest, RemovedAddTest) +{ + constexpr sc_addr addr = {1, 1}; + EXPECT_TRUE(sc_transaction_buffer_removed_add(buffer, &addr)); + EXPECT_EQ(buffer->deleted_elements->size, 1u); + + sc_uint32 addr_hash = SC_ADDR_LOCAL_TO_INT(addr); + EXPECT_EQ(reinterpret_cast(buffer->deleted_elements->begin->data), addr_hash); + + // Test duplicate addition + EXPECT_TRUE(sc_transaction_buffer_removed_add(buffer, &addr)); + EXPECT_EQ(buffer->deleted_elements->size, 1u); // Size should not increase +} + +TEST_F(ScTransactionBufferTest, ModifiedAddTest) +{ + sc_addr addr; + sc_storage_allocate_new_element(m_ctx->GetRealContext(), &addr); + + sc_element* element; + sc_storage_get_element_by_addr(addr, &element); + + element->incoming_arcs_count++; + sc_element_data const new_data = { + .flags = element->flags, + .first_out_arc = element->first_out_arc, + .first_in_arc = element->first_in_arc, + #ifdef SC_OPTIMIZE_SEARCHING_INCOMING_CONNECTORS_FROM_STRUCTURES + .first_in_arc_from_structure = element->first_in_arc_from_structure, + #endif + .arc = element->arc, + .incoming_arcs_count = element->incoming_arcs_count, + .outgoing_arcs_count = element->outgoing_arcs_count + }; + + EXPECT_TRUE(sc_transaction_buffer_modified_add(buffer, &addr, &new_data)); + EXPECT_EQ(buffer->modified_elements->size, 1u); + + sc_uint32 const addr_hash = SC_ADDR_LOCAL_TO_INT(addr); + auto const pair = static_cast(buffer->modified_elements->begin->data); + EXPECT_EQ(reinterpret_cast(pair->first), addr_hash); +} + +TEST_F(ScTransactionBufferTest, ContentSetTest) +{ + constexpr sc_addr addr = {1, 4}; + sc_stream* stream = sc_stream_memory_new("test", 4, SC_STREAM_FLAG_READ, SC_FALSE); + ASSERT_NE(stream, nullptr); + + EXPECT_TRUE(sc_transaction_buffer_content_set(buffer, &addr, stream)); + EXPECT_EQ(buffer->content_changes->size, 1u); + + constexpr sc_uint32 addr_hash = SC_ADDR_LOCAL_TO_INT(addr); + sc_iterator* it = sc_list_iterator(buffer->content_changes); + sc_bool found = SC_FALSE; + + while (sc_iterator_next(it)) + { + sc_pair* pair = static_cast(sc_iterator_get(it)); + if (reinterpret_cast(pair->first) == addr_hash) + { + EXPECT_EQ(pair->second, stream); + found = SC_TRUE; + break; + } + } + sc_iterator_destroy(it); + EXPECT_TRUE(found); + + // Test content update + sc_stream* new_stream = sc_stream_memory_new("new_test", 8, SC_STREAM_FLAG_READ, SC_FALSE); + EXPECT_TRUE(sc_transaction_buffer_content_set(buffer, &addr, new_stream)); + EXPECT_EQ(buffer->content_changes->size, 1u); // Size should not increase + + it = sc_list_iterator(buffer->content_changes); + found = SC_FALSE; + while (sc_iterator_next(it)) + { + auto const pair = static_cast(sc_iterator_get(it)); + if (reinterpret_cast(pair->first) == addr_hash) + { + EXPECT_EQ(pair->second, new_stream); + found = SC_TRUE; + break; + } + } + sc_iterator_destroy(it); + EXPECT_TRUE(found); +} \ No newline at end of file diff --git a/sc-memory/sc-core/tests/units/transaction/test_sc_transaction_manager.cpp b/sc-memory/sc-core/tests/units/transaction/test_sc_transaction_manager.cpp new file mode 100644 index 000000000..3b058e9e2 --- /dev/null +++ b/sc-memory/sc-core/tests/units/transaction/test_sc_transaction_manager.cpp @@ -0,0 +1,148 @@ +#include +#include +#include + +extern "C" +{ +#include "sc-store/sc-base/sc_thread.h" +#include +#include +} + +class ScTransactionManagerTest : public ScMemoryTest +{ +protected: + sc_memory_transaction_manager * transaction_manager = nullptr; + + void SetUp() override + { + ScMemoryTest::SetUp(); + transaction_manager = sc_memory_transaction_manager_get(); + if (!sc_memory_transaction_manager_is_initialized()) + { + sc_memory_transaction_manager * manager = sc_mem_new(sc_memory_transaction_manager, 1); + sc_memory_transaction_manager_initialize(manager); + } + transaction_manager = sc_memory_transaction_manager_get(); + } + + void TearDown() override + { + sc_memory_transaction_shutdown(); + ScMemoryTest::TearDown(); + } +}; + +// Test Initialization +TEST_F(ScTransactionManagerTest, Initialization) +{ + ASSERT_NE(transaction_manager, nullptr); + EXPECT_TRUE(sc_memory_transaction_manager_is_initialized()); +} + +// Test Shutdown +TEST_F(ScTransactionManagerTest, Shutdown) +{ + sc_memory_transaction_shutdown(); + EXPECT_FALSE(sc_memory_transaction_manager_is_initialized()); + EXPECT_EQ(sc_memory_transaction_manager_get(), nullptr); +} + +// Test Transaction Creation +TEST_F(ScTransactionManagerTest, TransactionNew) +{ + sc_transaction * txn1 = sc_memory_transaction_new(m_ctx->GetRealContext()); + EXPECT_NE(txn1, nullptr); + EXPECT_EQ(txn1->transaction_id, 0); // First transaction should have ID 0 + + sc_transaction * txn2 = sc_memory_transaction_new(m_ctx->GetRealContext()); + EXPECT_NE(txn2, nullptr); + EXPECT_EQ(txn2->transaction_id, 1); // Second transaction should have ID 1 +} + +// Test Transaction Addition to Queue +TEST_F(ScTransactionManagerTest, TransactionAdd) +{ + sc_transaction * txn = sc_memory_transaction_new(m_ctx->GetRealContext()); + EXPECT_NE(txn, nullptr); + sc_transaction_manager_transaction_add(txn); + + sc_monitor_acquire_read(transaction_manager->monitor); + EXPECT_EQ(transaction_manager->transaction_queue->size, 1); + auto * queued_txn = static_cast(sc_queue_front(transaction_manager->transaction_queue)); + EXPECT_EQ(queued_txn, txn); + sc_monitor_release_read(transaction_manager->monitor); +} + +// Test Transaction Processing +TEST_F(ScTransactionManagerTest, TransactionProcessing) +{ + sc_transaction * txn = sc_memory_transaction_new(m_ctx->GetRealContext()); + EXPECT_NE(txn, nullptr); + + // Add an element to txn->elements to simulate real usage + sc_hash_table_insert(txn->elements, (void*)1, (void*)1); + + sc_transaction_manager_transaction_add(txn); + + // Wait for transaction to be processed + while (transaction_manager->transaction_queue->size > 0) + { + sc_thread_sleep(10); // Sleep for 10ms + } + + EXPECT_EQ(txn->state, SC_TRANSACTION_EXECUTED); +} + +// Test Element Intersection +TEST_F(ScTransactionManagerTest, ElementIntersection) +{ + sc_transaction * txn = sc_memory_transaction_new(m_ctx->GetRealContext()); + EXPECT_NE(txn, nullptr); + + // Add intersecting element + sc_hash_table_insert(txn->elements, (void*)1, (void*)1); + sc_hash_table_insert(transaction_manager->processed_elements, (void*)1, (void*)1); + + EXPECT_TRUE(sc_transaction_elements_intersect(txn, transaction_manager->processed_elements)); + + // Remove intersection + sc_hash_table_remove(txn->elements, (void*)1); + EXPECT_FALSE(sc_transaction_elements_intersect(txn, transaction_manager->processed_elements)); + + sc_hash_table_destroy(transaction_manager->processed_elements); +} + +// Test Thread Safety with Multiple Transactions +TEST_F(ScTransactionManagerTest, ThreadSafety) +{ + constexpr int num_transactions = 10; + sc_transaction * transactions[num_transactions]; + + for (int i = 0; i < num_transactions; ++i) + { + transactions[i] = sc_memory_transaction_new(m_ctx->GetRealContext()); + EXPECT_NE(transactions[i], nullptr); + sc_hash_table_insert(transactions[i]->elements, (void*)(i + 1), (void*)(i + 1)); // Unique element per transaction + sc_transaction_manager_transaction_add(transactions[i]); + } + + // Wait for all transactions to be processed + while (transaction_manager->transaction_queue->size > 0) + { + sc_thread_sleep(10); // Sleep for 10ms + } + + for (int i = 0; i < num_transactions; ++i) + { + EXPECT_EQ(transactions[i]->state, SC_TRANSACTION_EXECUTED); + } +} + +// Test Transaction Manager Destroy +TEST_F(ScTransactionManagerTest, TransactionManagerDestroy) +{ + sc_memory_transaction_shutdown(); + EXPECT_FALSE(sc_memory_transaction_manager_is_initialized()); + EXPECT_EQ(sc_memory_transaction_manager_get(), nullptr); +} \ No newline at end of file diff --git a/sc-memory/sc-core/tests/units/transaction/test_sc_transaction_memory.cpp b/sc-memory/sc-core/tests/units/transaction/test_sc_transaction_memory.cpp new file mode 100644 index 000000000..e69de29bb