diff --git a/examples/tilt/.gitignore b/examples/tilt/.gitignore new file mode 100644 index 00000000..119b4dfc --- /dev/null +++ b/examples/tilt/.gitignore @@ -0,0 +1 @@ +/*/build*/* \ No newline at end of file diff --git a/examples/tilt/README.md b/examples/tilt/README.md index ca1deb68..6d632b35 100644 --- a/examples/tilt/README.md +++ b/examples/tilt/README.md @@ -1,10 +1,9 @@ -# Tutorial: Tilt +# Tilt ## Clone libvsync and tilt repositories ```bash cd examples/tilt/ -mkdir deps/ cd deps/ git clone https://github.com/open-s4c/libvsync.git git clone https://github.com/open-s4c/tilt.git @@ -18,8 +17,25 @@ cd ../ . ./venv/bin/activate ``` -## Run simple test campaign +## Campaigns + +### Simple Mutex test + +Runs a simple campaign to run Tilt locks. ```bash -./campaign_tilt.py +./campaign_test.py ``` + +### Reciprocating lock benchmarks + +Runs a campaign that runs the benchmarks described in the Reciprocating Locks paper, using the Tilt. + +```bash +./campaign_reciprocating_locks.py +``` + +## Todo + +- [ ] Fix implemenation of CLH lock +- [ ] Fix implementation of the atomic benchmark diff --git a/examples/tilt/bench/CMakeLists.txt b/examples/tilt/bench/CMakeLists.txt index 85445cb2..38bdb329 100644 --- a/examples/tilt/bench/CMakeLists.txt +++ b/examples/tilt/bench/CMakeLists.txt @@ -1,9 +1,28 @@ +# Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +# SPDX-License-Identifier: MIT + cmake_minimum_required(VERSION 3.10) -project(PthreadMutexExample) +project(PthreadMutex) +# project(PthreadMutex LANGUAGES C CXX) + +# Build options +set(NB_THREADS 4 CACHE STRING "Number of threads increasing the counter") +set(RUN_DURATION_SECONDS 10 CACHE STRING "Time during which the threads will increase the counter") set(CMAKE_C_STANDARD 99) +# set(CMAKE_CXX_STANDARD 11) + +configure_file(${CMAKE_SOURCE_DIR}/include/config.h.in ${CMAKE_BINARY_DIR}/include/config.h) + +include_directories(${CMAKE_CURRENT_BINARY_DIR}/include) -add_executable(mutex mutex.c) +add_executable(test_mutex test_mutex.c) +add_executable(bench_mutex bench_mutex.cpp) +add_executable(bench_mutex_moderate bench_mutex_moderate.cpp) +# add_executable(bench_atomic bench_atomic.cpp) find_package(Threads REQUIRED) -target_link_libraries(mutex Threads::Threads) +target_link_libraries(test_mutex Threads::Threads) +target_link_libraries(bench_mutex Threads::Threads) +target_link_libraries(bench_mutex_moderate Threads::Threads) +# target_link_libraries(bench_atomic Threads::Threads) diff --git a/examples/tilt/bench/bench_atomic.cpp b/examples/tilt/bench/bench_atomic.cpp new file mode 100644 index 00000000..929cd5f5 --- /dev/null +++ b/examples/tilt/bench/bench_atomic.cpp @@ -0,0 +1,85 @@ +// Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* defines NB_THREADS, RUN_DURATION_SECONDS */ + +#define IMPLICIT_INIT + +pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + +struct S { + uint32_t a, b, c, d, e; +}; +std::atomic shared; +std::atomic done{false}; +std::atomic iterations_total{0}; + +void* worker(void* arg) { + S local = {1, 2, 3, 4, 5}; + uint64_t iterations_local = 0; + while (!atomic_load_explicit(&done, std::memory_order_relaxed)) { // TODO: fix + pthread_mutex_lock(&lock); + // Critical section + atomic_exchange_explicit(&shared, local, std::memory_order_seq_cst); // exchange with global + + pthread_mutex_unlock(&lock); + + // Non-critical section + // (sleep or dummy ops to simulate delay) + ++iterations_local; + } + atomic_fetch_add_explicit(&iterations_total, iterations_local, std::memory_order_relaxed); + return NULL; +} + +int main() { + #if !defined(IMPLICIT_INIT) + if (pthread_mutex_init(&lock, NULL) != 0) { + printf("Mutex initialization failed\n"); + return 1; + } + #endif /* EXPLICIT_INIT */ + + // Initialize shared atomic with zeros + S zeroes = {0, 0, 0, 0, 0}; + shared.store(zeroes, std::memory_order_relaxed); // TODO: fix + //atomic_store_explicit(&shared, {0, 0, 0, 0, 0}, std::memory_order_relaxed); + + pthread_t threads[NB_THREADS]; + unsigned long thread_iterations[NB_THREADS]; + for (int i = 0; i < NB_THREADS; ++i) + pthread_create(&threads[i], NULL, worker, NULL); + + // Run for a fixed duration + std::this_thread::sleep_for(std::chrono::seconds(RUN_DURATION_SECONDS)); + atomic_store_explicit(&done, 1, std::memory_order_relaxed); + + for (int i = 0; i < NB_THREADS; ++i) { + void* return_value; + pthread_join(threads[i], &return_value); + thread_iterations[i] = (unsigned long) return_value; + } + + std::cout << "total_iterations=" << iterations_total.load(); + std::cout << ";duration=" << RUN_DURATION_SECONDS; + std::cout << ";nb_threads=" << NB_THREADS; + + // print per thread iterations + for (size_t k = 0u; k < NB_THREADS; ++k) { + std::cout << ";thread_" << k << "=" << thread_iterations[k]; + } + + std::cout << "\n"; + pthread_mutex_destroy(&lock); + + return 0; +} diff --git a/examples/tilt/bench/bench_mutex.cpp b/examples/tilt/bench/bench_mutex.cpp new file mode 100644 index 00000000..4447354e --- /dev/null +++ b/examples/tilt/bench/bench_mutex.cpp @@ -0,0 +1,79 @@ +// Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include /* defines NB_THREADS, RUN_DURATION_SECONDS and lock_* operations and types. */ + +#define IMPLICIT_INIT + +pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + +std::mt19937 prng{42}; // shared global PRNG +std::atomic done{false}; +std::atomic iterations_total{0}; + +void* worker(void* arg) { + uint64_t iterations_local = 0; + while (!atomic_load_explicit(&done, std::memory_order_relaxed)) { + pthread_mutex_lock(&lock); + // Critical section + prng(); // one step of PRNG in critical section + + pthread_mutex_unlock(&lock); + + // Non-critical section + // (sleep or dummy ops to simulate delay) + ++iterations_local; + } + atomic_fetch_add_explicit(&iterations_total, iterations_local, std::memory_order_relaxed); + void *result = (void*) iterations_local; + return result; +} + +int main() { + #if !defined(IMPLICIT_INIT) + if (pthread_mutex_init(&lock, NULL) != 0) { + printf("Mutex initialization failed\n"); + return 1; + } + #endif /* EXPLICIT_INIT */ + + pthread_t threads[NB_THREADS]; + unsigned long thread_iterations[NB_THREADS]; + for (int i = 0; i < NB_THREADS; ++i) + pthread_create(&threads[i], NULL, worker, NULL); + + //sleep(RUN_DURATION_SECONDS); + std::this_thread::sleep_for(std::chrono::seconds(RUN_DURATION_SECONDS)); + atomic_store_explicit(&done, 1, std::memory_order_relaxed); + + + for (int i = 0; i < NB_THREADS; ++i) { + void* return_value; + pthread_join(threads[i], &return_value); + thread_iterations[i] = (unsigned long) return_value; + } + + std::cout << "total_iterations=" << iterations_total.load(); + std::cout << ";duration=" << RUN_DURATION_SECONDS; + std::cout << ";nb_threads=" << NB_THREADS; + + // print per thread iterations + for (size_t k = 0u; k < NB_THREADS; ++k) { + std::cout << ";thread_" << k << "=" << thread_iterations[k]; + } + + std::cout << "\n"; + pthread_mutex_destroy(&lock); + + return 0; +} diff --git a/examples/tilt/bench/bench_mutex_moderate.cpp b/examples/tilt/bench/bench_mutex_moderate.cpp new file mode 100644 index 00000000..02f615be --- /dev/null +++ b/examples/tilt/bench/bench_mutex_moderate.cpp @@ -0,0 +1,93 @@ +// Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include /* defines NB_THREADS, RUN_DURATION_SECONDS */ + +#define IMPLICIT_INIT + +pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER; + +std::mt19937 prng_global{42}; // shared global PRNG +std::atomic done{false}; +std::atomic iterations_total{0}; + +volatile uint32_t prng_sink_global = 0; // Necessary to prevent optimization of the non-critical section + +void* worker(void* arg) { + uint64_t iterations_local = 0; + + // Setup local PRNG for non-critical section + std::mt19937 prng_local{42}; // local PRNG + std::uniform_int_distribution dist(0, 249); + uint32_t prng_sink = 0; // value to prevent optimization + + while (!atomic_load_explicit(&done, std::memory_order_relaxed)) { + + // Critical section + pthread_mutex_lock(&lock); + prng_global(); // one step of PRNG in critical section + pthread_mutex_unlock(&lock); + + // Non-critical section + int steps = dist(prng_local); + for (int i = 0; i < steps; ++i) + prng_sink += prng_local(); + + ++iterations_local; + } + + // Consume final value so optimizer can't skip + prng_sink_global += prng_sink; + + atomic_fetch_add_explicit(&iterations_total, iterations_local, std::memory_order_relaxed); + + void *result = (void*) iterations_local; + return result; +} + +int main() { + #if !defined(IMPLICIT_INIT) + if (pthread_mutex_init(&lock, NULL) != 0) { + printf("Mutex initialization failed\n"); + return 1; + } + #endif /* EXPLICIT_INIT */ + + pthread_t threads[NB_THREADS]; + unsigned long thread_iterations[NB_THREADS]; + for (int i = 0; i < NB_THREADS; ++i) + pthread_create(&threads[i], NULL, worker, NULL); + + // Run for a fixed duration + std::this_thread::sleep_for(std::chrono::seconds(RUN_DURATION_SECONDS)); + atomic_store_explicit(&done, 1, std::memory_order_relaxed); + + for (int i = 0; i < NB_THREADS; ++i) { + void* return_value; + pthread_join(threads[i], &return_value); + thread_iterations[i] = (unsigned long) return_value; + } + + std::cout << "total_iterations=" << iterations_total.load(); + std::cout << ";duration=" << RUN_DURATION_SECONDS; + std::cout << ";nb_threads=" << NB_THREADS; + + // print per thread iterations + for (size_t k = 0u; k < NB_THREADS; ++k) { + std::cout << ";thread_" << k << "=" << thread_iterations[k]; + } + + std::cout << "\n"; + pthread_mutex_destroy(&lock); + + return 0; +} diff --git a/examples/tilt/bench/include/config.h.in b/examples/tilt/bench/include/config.h.in new file mode 100644 index 00000000..42742cb7 --- /dev/null +++ b/examples/tilt/bench/include/config.h.in @@ -0,0 +1,13 @@ +/* + * Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. + * SPDX-License-Identifier: MIT + */ + +#ifndef CONFIG_H +#define CONFIG_H + +#define NB_THREADS @NB_THREADS@ + +#define RUN_DURATION_SECONDS @RUN_DURATION_SECONDS@ + +#endif /* CONFIG_H */ diff --git a/examples/tilt/bench/mutex.c b/examples/tilt/bench/test_mutex.c similarity index 84% rename from examples/tilt/bench/mutex.c rename to examples/tilt/bench/test_mutex.c index 2d37f208..25602b35 100644 --- a/examples/tilt/bench/mutex.c +++ b/examples/tilt/bench/test_mutex.c @@ -1,3 +1,6 @@ +// Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +// SPDX-License-Identifier: MIT + #include #include #include diff --git a/examples/tilt/campaign_reciprocating_locks.py b/examples/tilt/campaign_reciprocating_locks.py new file mode 100755 index 00000000..4d3e9aff --- /dev/null +++ b/examples/tilt/campaign_reciprocating_locks.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +# SPDX-License-Identifier: MIT +""" +Campaign to run the Reciprocating locks benchmarks using Tilt. +""" + + +from tiltlib import TiltLib +from mutex_bench import MutexBench + +from benchkit.campaign import CampaignCartesianProduct, CampaignSuite +from benchkit.utils.dir import caller_dir + +campaign_dir = caller_dir() +tilt_locks_dir = campaign_dir / "locks" +bench_src_dir = campaign_dir / "bench" +vsync_dir = (tilt_locks_dir / "../deps/libvsync/").resolve() + +NB_RUNS = 7 +LOCKS = [ + # "clhlock", # TODO: fix Tilt implementation + "hemlock", + "mcslock", + "reciplock", + "ticketlock", + "twalock", +] + +BENCHMARK_DURATION_SECONDS = 10 + + +def get_campaign_mutex(tiltlib, benchmark_name: str) -> CampaignCartesianProduct: + return CampaignCartesianProduct( + name="tilt", + benchmark=MutexBench( + src_dir=bench_src_dir, + shared_libs=[tiltlib], + ), + nb_runs=NB_RUNS, + variables={ + "lock": LOCKS, + "nb_threads": range(1, 17), # 1-16 threads + }, + constants={ + "benchmark_name": benchmark_name, + "benchmark_duration_seconds": BENCHMARK_DURATION_SECONDS, + }, + debug=False, + gdb=False, + enable_data_dir=True, + continuing=False, + benchmark_duration_seconds=BENCHMARK_DURATION_SECONDS, + pretty={ + "lock": { + # "clhlock": "CLH lock", + "hemlock": "Hemlock", + "mcslock": "MCS lock", + "reciplock": "Reciprocating lock", + "ticketlock": "Ticketlock", + "twalock": "TWA lock", + }, + }, + ) + +def main() -> None: + tiltlib = TiltLib(tilt_locks_dir=tilt_locks_dir) + tiltlib.build() + + campaigns = [ + get_campaign_mutex(tiltlib, "bench_mutex"), # High contention + get_campaign_mutex(tiltlib, "bench_mutex_moderate"), # Moderate contention + ] + + suite = CampaignSuite(campaigns=campaigns) + suite.print_durations() + suite.run_suite() + + suite.generate_graph( + plot_name="lineplot", + x="nb_threads", + y="total_iterations", + hue="lock", + ) + + +if __name__ == "__main__": + main() diff --git a/examples/tilt/campaign_test.py b/examples/tilt/campaign_test.py new file mode 100755 index 00000000..25ce33a0 --- /dev/null +++ b/examples/tilt/campaign_test.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python3 +# Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +# SPDX-License-Identifier: MIT +""" +Minimal example to run tilt locks. +""" + + +from tiltlib import TiltLib +from test_bench import SimpleMutexTestBench as TestBench + +from benchkit.campaign import CampaignCartesianProduct, CampaignSuite +from benchkit.utils.dir import caller_dir + +campaign_dir = caller_dir() +tilt_locks_dir = campaign_dir / "locks" +bench_src_dir = campaign_dir / "bench" +vsync_dir = (tilt_locks_dir / "../deps/libvsync/").resolve() + +NB_RUNS = 1 +LOCKS = [ + # "clhlock", # TODO: fix Tilt implementation + "hemlock", + "mcslock", + "reciplock", + "ticketlock", + "twalock", +] + + +def get_campaign_test(tiltlib) -> CampaignCartesianProduct: + benchmark_name = "test_mutex" + return CampaignCartesianProduct( + name="tilt", + benchmark=TestBench( + src_dir=bench_src_dir, + shared_libs=[tiltlib], + ), + nb_runs=NB_RUNS, + variables={ + "lock": LOCKS + ["taslock", "caslock", "vcaslock-nolse", "vcaslock-lse"], + }, + constants={ + "benchmark_name": benchmark_name, + }, + debug=False, + gdb=False, + enable_data_dir=True, + continuing=False, + ) + +def main() -> None: + tiltlib = TiltLib(tilt_locks_dir=tilt_locks_dir) + tiltlib.build() + + campaigns = [ + get_campaign_test(tiltlib), + ] + + suite = CampaignSuite(campaigns=campaigns) + suite.print_durations() + suite.run_suite() + + +if __name__ == "__main__": + main() diff --git a/examples/tilt/campaign_tilt.py b/examples/tilt/campaign_tilt.py deleted file mode 100755 index 849857cc..00000000 --- a/examples/tilt/campaign_tilt.py +++ /dev/null @@ -1,49 +0,0 @@ -#!/usr/bin/env python3 -# Copyright (C) 2024 Vrije Universiteit Brussel. All rights reserved. -# SPDX-License-Identifier: MIT -""" -Minimal example to run tilt locks. -""" - - -from tiltlib import SimpleMutexTestBench as Bench -from tiltlib import TiltLib - -from benchkit.campaign import CampaignCartesianProduct, CampaignSuite -from benchkit.utils.dir import caller_dir - -campaign_dir = caller_dir() -tilt_locks_dir = campaign_dir / "locks" -bench_src_dir = campaign_dir / "bench" -vsync_dir = (tilt_locks_dir / "../deps/libvsync/").resolve() - - -def main() -> None: - tiltlib = TiltLib(tilt_locks_dir=tilt_locks_dir) - tiltlib.build() - - campaigns = [ - CampaignCartesianProduct( - name="tilt", - benchmark=Bench( - src_dir=bench_src_dir, - shared_libs=[tiltlib], - ), - nb_runs=1, - variables={ - "lock": ["", "taslock", "caslock", "vcaslock-nolse", "vcaslock-lse"], - }, - constants=None, - debug=False, - gdb=False, - enable_data_dir=True, - continuing=False, - ), - ] - suite = CampaignSuite(campaigns=campaigns) - suite.print_durations() - suite.run_suite() - - -if __name__ == "__main__": - main() diff --git a/examples/tilt/deps/reciplock/CMakeLists.txt b/examples/tilt/deps/reciplock/CMakeLists.txt new file mode 100644 index 00000000..65c9b7ff --- /dev/null +++ b/examples/tilt/deps/reciplock/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +# SPDX-License-Identifier: MIT + +cmake_minimum_required(VERSION 3.10) +project( + reciplock_headers + LANGUAGES C + VERSION 2.1.0 +) + +include_directories(include) +add_library(reciplock_headers INTERFACE) +target_include_directories(reciplock_headers INTERFACE include) diff --git a/examples/tilt/deps/reciplock/include/reciplock.h b/examples/tilt/deps/reciplock/include/reciplock.h new file mode 100644 index 00000000..7066ac86 --- /dev/null +++ b/examples/tilt/deps/reciplock/include/reciplock.h @@ -0,0 +1,247 @@ +// Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +// SPDX-License-Identifier: MIT + +#ifndef RECIP_RECIPLOCK_H +#define RECIP_RECIPLOCK_H +/******************************************************************************* + * @file reciplock.h + * @brief one sentence that describes the algorithm. + * + * @ingroup lock_free // if your algo belongs to certain groups you + * // can list them here separated with spaces + * + * + * // add detailed information about the algorithm + * // operating conditions etc. + * + * + * @example + * @include eg_reciplock.c + * * + * @cite + * David Dice, Alex Kogan - [Reciprocating Locks. arXiv:2501.02380] + ******************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#define TLS_IMPLEMENTATION 1 + +// a thread knows only the identity of its immediate neighbor in the arrival segment +typedef struct reciplock_node_s { + _Atomic(struct reciplock_node_s *) next; // next node in waiting +} reciplock_node_t; + +/** Reciprocating lock data structure. */ +typedef struct reciplock_s { + _Atomic(reciplock_node_t *) tail; // tail of the arrival segment +} reciplock_t; + +// Spinner, if not nullptr then we can enter critical section +static __thread reciplock_node_t tls_wait_node; // thread-specific waiting element + +#if TLS_IMPLEMENTATION +static __thread reciplock_node_t *tls_successor; +static __thread reciplock_node_t *tls_end_of_segment; +#endif + +// encoding of the "simple-locked" state, lock is active but nothing in entry segment +static reciplock_node_t * const LOCKED_EMPTY = (reciplock_node_t *)(uintptr_t) 1; + +/** Initializer of `reciplock_t`. */ +#define RECIPLOCK_INIT() \ +{ \ + .tail = ATOMIC_VAR_INIT(NULL) \ +} + +/** + * Initializes the reciprocating lock. + * + * @param lock address of reciplock_t object. + * + * @note alternatively use `RECIPLOCK_INIT`. + */ +static inline void +reciplock_init(reciplock_t *lock) +{ + atomic_store_explicit( &lock->tail, NULL, memory_order_release ); +} + +// segment_end and succ reflect context to be passed from Acquire to corresponding Release +// Could also pass the context via fields in the lock body or into TLS. + +/** + * Acquires the reciprocating lock. + * + * @param lock address of reciplock_t object. + * @param _segment_end address of reciplock_node_t object, associated with the calling + * thread/core. + * @return Pointer to successor node if one exists; NULL otherwise. + * + * Each thread uses its own tls_wait_node for participation. + * @remarks segment_end and successor node are used to pass context from Acquire to Release. + * Could also pass the context via fields in the lock body or into TLS. + */ +static inline reciplock_node_t * +#if TLS_IMPLEMENTATION +reciplock_acquire( reciplock_t *lock ) +#else +reciplock_acquire( reciplock_t *lock, reciplock_node_t **_segment_end ) +#endif +{ + // initializes its thread-specific waiting element in anticipation of potential contention + atomic_store_explicit( &tls_wait_node.next, NULL, memory_order_release ); + + reciplock_node_t *succ = NULL; + reciplock_node_t *segment_end = &tls_wait_node; // currently nullptr + + // swaps the address of wait_element into lock's arrival word; + reciplock_node_t *const tail_prev = atomic_exchange( &lock->tail, &tls_wait_node ); + assert( tail_prev != &tls_wait_node ); + + // Lock is held + if ( tail_prev != NULL ) { + // coerce LOCKED_EMPTY to null + // succ will be our successor when we subsequently release + succ = (reciplock_node_t *)(((uintptr_t) tail_prev) & ~1); + assert(succ != &tls_wait_node); + + // Spin until our predecessor grants us access. + for ( ;; ) { + segment_end = atomic_load_explicit( &tls_wait_node.next, memory_order_acquire ); + if ( segment_end != NULL ) break; + //Pause(); + } + assert( segment_end != &tls_wait_node ); + + // Detect logical end-of-segment terminus address + if ( succ == segment_end ) { + succ = NULL; // quash + segment_end = LOCKED_EMPTY; + } + } +#if TLS_IMPLEMENTATION + tls_successor = succ; + tls_end_of_segment = segment_end; +#else + *_segment_end = segment_end; +#endif + return succ; +} + +#if TLS_IMPLEMENTATION +/** + * Releases the reciprocating lock. + * + * @param lock address of reciplock_t object. + */ +static inline void +reciplock_release( reciplock_t *lock ) { + assert( tls_end_of_segment != NULL ); + assert( atomic_load_explicit(&lock->tail, memory_order_acquire) != NULL ); + + if ( tls_successor != NULL ) { + assert( atomic_load(&tls_successor->next) == NULL ); + + // Notify successor that it may proceed. + atomic_store_explicit( &tls_successor->next, tls_end_of_segment, memory_order_release ); + return; + } + + assert( tls_end_of_segment == LOCKED_EMPTY || tls_end_of_segment == &tls_wait_node ); +#if 0 + reciplock_node_t * v = tls_end_of_segment; + + // empty entry segment + if ( atomic_compare_exchange_strong_explicit( &lock->tail, &v, NULL, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) ) { + // uncontended fast-path return + return; + } +#else + // Fast path: if the tail is empty, we can just set it to NULL + if ( atomic_load_explicit( &lock->tail, memory_order_acquire ) == tls_end_of_segment ) { + reciplock_node_t *v = tls_end_of_segment; + if ( atomic_compare_exchange_strong_explicit( &lock->tail, &v, NULL, memory_order_seq_cst, memory_order_seq_cst ) ) { + // uncontended fast-path return + return; + } + } +#endif + + // Slow path: tail is not empty, we need to detach the arrival segment + reciplock_node_t *w = atomic_exchange( &lock->tail, LOCKED_EMPTY ); // detach arrival segment + assert( w != NULL ); + assert( w != LOCKED_EMPTY ); + assert( w != &tls_wait_node ); + assert( atomic_load_explicit( &w->next, memory_order_acquire ) == NULL ); + + // Let the new arrival know the segment's boundary + atomic_store_explicit( &w->next, tls_end_of_segment, memory_order_release ); // pass ownership; pass address of wait_element as end of segment marker +} +#else +/** + * Releases the reciprocating lock. + * + * @param lock address of reciplock_t object. + * @param end_of_segment address of reciplock_node_t object, associated with the calling + * thread/core. + * @param succ address of successor node (if one exists), to which control is handed off. + * @remarks segment_end and successor node are used to pass context from Acquire to Release. + * Could also pass the context via fields in the lock body or into TLS. + */ +static inline void +reciplock_release( reciplock_t *lock, reciplock_node_t *end_of_segment, reciplock_node_t *succ ) { + assert( end_of_segment != NULL ); + assert( atomic_load_explicit(&lock->tail, memory_order_acquire) != NULL ); + + if ( succ != NULL ) { + assert( atomic_load(&succ->next) == NULL ); + + // Notify successor that it may proceed. + atomic_store_explicit( &succ->next, end_of_segment, memory_order_release ); + return; + } + + assert( end_of_segment == LOCKED_EMPTY || end_of_segment == &tls_wait_node ); +#if 0 + reciplock_node_t * v = end_of_segment; + + // empty entry segment + if ( atomic_compare_exchange_strong_explicit( &lock->tail, &v, NULL, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST ) ) { + // uncontended fast-path return + return; + } +#else + // Fast path: if the tail is empty, we can just set it to NULL + if ( atomic_load_explicit( &lock->tail, memory_order_acquire ) == end_of_segment ) { + reciplock_node_t *v = end_of_segment; + if ( atomic_compare_exchange_strong_explicit( &lock->tail, &v, NULL, memory_order_seq_cst, memory_order_seq_cst ) ) { + // uncontended fast-path return + return; + } + } +#endif + + // Slow path: tail is not empty, we need to detach the arrival segment + reciplock_node_t *w = atomic_exchange( &lock->tail, LOCKED_EMPTY ); // detach arrival segment + assert( w != NULL ); + assert( w != LOCKED_EMPTY ); + assert( w != &tls_wait_node ); + assert( atomic_load_explicit( &w->next, memory_order_acquire ) == NULL ); + + // Let the new arrival know the segment's boundary + atomic_store_explicit( &w->next, end_of_segment, memory_order_release ); // pass ownership; pass address of wait_element as end of segment marker +} +#endif + +//void __attribute__((noinline)) ctor() { +// atomic_store_explicit( &lock.arrivals, NULL, memory_order_release ); +//} // ctor +// +//void __attribute__((noinline)) dtor() { +//} // dtor +#endif //RECIP_RECIPLOCK_H diff --git a/examples/tilt/kit/mutex_bench.py b/examples/tilt/kit/mutex_bench.py new file mode 100644 index 00000000..2418ca68 --- /dev/null +++ b/examples/tilt/kit/mutex_bench.py @@ -0,0 +1,153 @@ +# Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +# SPDX-License-Identifier: MIT + +import pathlib +from typing import Any, Dict, Iterable, List + +from benchkit.benchmark import ( + Benchmark, + CommandAttachment, + CommandWrapper, + PostRunHook, + PreRunHook, + SharedLib, +) +from benchkit.platforms import Platform +from benchkit.utils.dir import caller_dir +from benchkit.utils.types import PathType + +from tiltlib import cmake_configure_build + +class MutexBench(Benchmark): + """Benchmark object for the mutex benchmark as described in the reciprocating locks paper.""" + def __init__( + self, + src_dir: PathType = "", + command_wrappers: Iterable[CommandWrapper] = (), + command_attachments: Iterable[CommandAttachment] = (), + shared_libs: Iterable[SharedLib] = (), + pre_run_hooks: Iterable[PreRunHook] = (), + post_run_hooks: Iterable[PostRunHook] = (), + platform: Platform = None, + ) -> None: + super().__init__( + command_wrappers=command_wrappers, + command_attachments=command_attachments, + shared_libs=shared_libs, + pre_run_hooks=pre_run_hooks, + post_run_hooks=post_run_hooks, + ) + if platform is not None: + self.platform = platform + if src_dir: + self._src_dir = pathlib.Path(src_dir).resolve() + else: + self._src_dir = (caller_dir() / "../bench").resolve() + self._build_dir = self._src_dir / f"build-{self.platform.hostname}" + + @property + def bench_src_path(self) -> pathlib.Path: + return self._src_dir + + @staticmethod + def get_build_var_names() -> List[str]: + return [ + # "benchmark_duration_seconds", + # "lock", + "nb_threads", + ] + + @staticmethod + def get_run_var_names() -> List[str]: + return [ + # "benchmark_name", + # "lock", + ] + + def clean_bench(self) -> None: + pass + + # def prebuild_bench( + # self, + # **kwargs, + # ) -> int: + + # print("[INFO] Building bench...") + # cmake_configure_build( + # platform=self.platform, + # src_dir=self.bench_src_path, + # build_dir=self._build_dir, + # debug=self.must_debug(), + # make_suffix=self._parallel_make_str(), + # ) + # return 0 + + def prebuild_bench( + self, + **kwargs, + ) -> None: + pass + + def build_bench( + self, + nb_threads: int, + **kwargs, + ) -> None: + # pass + + print("[INFO] Building bench...") + duration = self._constants.get("benchmark_duration_seconds") + + command_args = [ + f"-DRUN_DURATION_SECONDS={duration}", + f"-DNB_THREADS={nb_threads}", + ] + cmake_configure_build( + platform=self.platform, + src_dir=self.bench_src_path, + build_dir=self._build_dir, + debug=self.must_debug(), + make_suffix=self._parallel_make_str(), + arg_list=command_args, + ) + return 0 + + def single_run( + self, + **kwargs, + ) -> str: + current_dir = self._build_dir + environment = self._preload_env( + **kwargs, + ) + print(f"[INFO] Running bench in {current_dir} with environment {environment}") + benchmark_name = self._constants.get("benchmark_name") + run_command = [f"./{benchmark_name}"] + + wrapped_run_command, wrapped_environment = self._wrap_command( + run_command=run_command, + environment=environment, + **kwargs, + ) + + output = self.run_bench_command( + run_command=run_command, + wrapped_run_command=wrapped_run_command, + current_dir=current_dir, + environment=environment, + wrapped_environment=wrapped_environment, + print_output=False, + ignore_ret_codes=(1,), + ) + print(output) + return output + + def parse_output_to_results( # pylint: disable=arguments-differ + self, + command_output: str, + run_variables: Dict[str, Any], + **_kwargs, + ) -> Dict[str, Any]: + key_seq_values = command_output.strip().split(";") + result_dict = dict(map(lambda s: s.split("="), key_seq_values)) + return result_dict diff --git a/examples/tilt/kit/test_bench.py b/examples/tilt/kit/test_bench.py new file mode 100644 index 00000000..61d419c2 --- /dev/null +++ b/examples/tilt/kit/test_bench.py @@ -0,0 +1,138 @@ +# Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +# SPDX-License-Identifier: MIT + +import pathlib +from typing import Any, Dict, Iterable, List + +from benchkit.benchmark import ( + Benchmark, + CommandAttachment, + CommandWrapper, + PostRunHook, + PreRunHook, + SharedLib, +) +from benchkit.platforms import Platform +from benchkit.utils.dir import caller_dir +from benchkit.utils.types import PathType + +from tiltlib import cmake_configure_build + +class SimpleMutexTestBench(Benchmark): + """Benchmark object for testing if our lock implementations for simple mutex operations work.""" + def __init__( + self, + src_dir: PathType = "", + command_wrappers: Iterable[CommandWrapper] = (), + command_attachments: Iterable[CommandAttachment] = (), + shared_libs: Iterable[SharedLib] = (), + pre_run_hooks: Iterable[PreRunHook] = (), + post_run_hooks: Iterable[PostRunHook] = (), + platform: Platform = None, + ) -> None: + super().__init__( + command_wrappers=command_wrappers, + command_attachments=command_attachments, + shared_libs=shared_libs, + pre_run_hooks=pre_run_hooks, + post_run_hooks=post_run_hooks, + ) + if platform is not None: + self.platform = platform + if src_dir: + self._src_dir = pathlib.Path(src_dir).resolve() + else: + self._src_dir = (caller_dir() / "../bench").resolve() + self._build_dir = self._src_dir / f"build-{self.platform.hostname}" + + @property + def bench_src_path(self) -> pathlib.Path: + return self._src_dir + + @staticmethod + def get_build_var_names() -> List[str]: + return [ + # "benchmark_duration_seconds", + # "lock", + ] + + @staticmethod + def get_run_var_names() -> List[str]: + return [ + # "benchmark_name", + # "lock", + ] + + def clean_bench(self) -> None: + pass + + # def prebuild_bench( + # self, + # **kwargs, + # ) -> int: + + # print("[INFO] Building bench...") + # cmake_configure_build( + # platform=self.platform, + # src_dir=self.bench_src_path, + # build_dir=self._build_dir, + # debug=self.must_debug(), + # make_suffix=self._parallel_make_str(), + # ) + # return 0 + + def prebuild_bench( + self, + **kwargs, + ) -> None: + pass + + def build_bench( + self, + **kwargs, + ) -> int: + cmake_configure_build( + platform=self.platform, + src_dir=self.bench_src_path, + build_dir=self._build_dir, + debug=self.must_debug(), + make_suffix=self._parallel_make_str(), + ) + return 0 + + def single_run( + self, + **kwargs, + ) -> str: + current_dir = self._build_dir + environment = self._preload_env( + **kwargs, + ) + benchmark_name = self._constants.get("benchmark_name") + run_command = [f"./{benchmark_name}"] + + wrapped_run_command, wrapped_environment = self._wrap_command( + run_command=run_command, + environment=environment, + **kwargs, + ) + + output = self.run_bench_command( + run_command=run_command, + wrapped_run_command=wrapped_run_command, + current_dir=current_dir, + environment=environment, + wrapped_environment=wrapped_environment, + print_output=False, + ignore_ret_codes=(1,), + ) + print(output) + return output + + def parse_output_to_results( # pylint: disable=arguments-differ + self, + command_output: str, + **_kwargs, + ) -> Dict[str, Any]: + result_dict = {} + return result_dict diff --git a/examples/tilt/kit/tiltlib.py b/examples/tilt/kit/tiltlib.py index cfcb6981..83daf929 100644 --- a/examples/tilt/kit/tiltlib.py +++ b/examples/tilt/kit/tiltlib.py @@ -1,187 +1,88 @@ -# Copyright (C) 2024 Vrije Universiteit Brussel. All rights reserved. +# Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. # SPDX-License-Identifier: MIT import pathlib import sys -from typing import Any, Dict, Iterable, List, Tuple +from typing import Tuple -from benchkit.benchmark import ( - Benchmark, - CommandAttachment, - CommandWrapper, - PostRunHook, - PreRunHook, - SharedLib, -) from benchkit.platforms import Platform from benchkit.sharedlibs import FromSourceSharedLib -from benchkit.utils.dir import caller_dir -from benchkit.utils.types import EnvironmentVariables, LdPreloadLibraries, PathType +from benchkit.utils.types import EnvironmentVariables, LdPreloadLibraries def cmake_configure_build( - platform: Platform, - src_dir: pathlib.Path, - build_dir: pathlib.Path, - debug: bool, - make_suffix: str, + platform: Platform, + src_dir: pathlib.Path, + build_dir: pathlib.Path, + debug: bool, + make_suffix: str, + arg_list: list[str] = [], ) -> None: - platform.comm.makedirs(path=build_dir, exist_ok=True) - - cmake_build_type = "Debug" if debug else "Release" - platform.comm.shell( - command=["cmake", f"-DCMAKE_BUILD_TYPE={cmake_build_type}", f"{src_dir}"], - current_dir=build_dir, - output_is_log=True, - ) - platform.comm.shell( - command=f"make{make_suffix}", - current_dir=build_dir, - output_is_log=True, - ) + platform.comm.makedirs(path=build_dir, exist_ok=True) + + cmake_build_type = "Debug" if debug else "Release" + command = [ + "cmake", + f"-DCMAKE_BUILD_TYPE={cmake_build_type}", + f"{src_dir}" + ] + + platform.comm.shell( + command=command + arg_list, + current_dir=build_dir, + output_is_log=True, + ) + platform.comm.shell( + command=f"make{make_suffix}", + current_dir=build_dir, + output_is_log=True, + ) class TiltLib(FromSourceSharedLib): - def __init__( - self, - tilt_locks_dir: pathlib.Path, - build_prefix: str = "build", - debug_mode: bool = False, - ) -> None: - super().__init__(src_path=tilt_locks_dir, debug_mode=debug_mode) - self.build_dir = self.src_path / f"{build_prefix}-{self.platform.hostname}" - - def build(self) -> None: - cmake_configure_build( - platform=self.platform, - src_dir=self.src_path, - build_dir=self.build_dir, - debug=self._debug_mode, - make_suffix=" -j1", - ) - - def preload( - self, - **kwargs, - ) -> Tuple[LdPreloadLibraries, EnvironmentVariables]: - ld_preloads, env_vars = super().preload( - **kwargs, - ) - - lock = kwargs.get("lock") - if lock: - lib_lockname = f"lib{lock}" - lib_path = (self.build_dir / f"{lib_lockname}.so").resolve() - if lib_path.is_file(): - ld_preloads.append(str(lib_path)) - else: - print( - ( - f"[WARNING] Tilt lib lock with name {lib_lockname} not found " - f"(in '{lib_path}'), not enabling." - ), - file=sys.stderr, - ) - raise ValueError( - f"Tilt lib lock with name {lib_lockname} not found in '{lib_path}'" - ) - - return ld_preloads, env_vars - - -class SimpleMutexTestBench(Benchmark): - def __init__( - self, - src_dir: PathType = "", - command_wrappers: Iterable[CommandWrapper] = (), - command_attachments: Iterable[CommandAttachment] = (), - shared_libs: Iterable[SharedLib] = (), - pre_run_hooks: Iterable[PreRunHook] = (), - post_run_hooks: Iterable[PostRunHook] = (), - platform: Platform = None, - ) -> None: - super().__init__( - command_wrappers=command_wrappers, - command_attachments=command_attachments, - shared_libs=shared_libs, - pre_run_hooks=pre_run_hooks, - post_run_hooks=post_run_hooks, - ) - if platform is not None: - self.platform = platform - if src_dir: - self._src_dir = pathlib.Path(src_dir).resolve() - else: - self._src_dir = (caller_dir() / "../bench").resolve() - self._build_dir = self._src_dir / f"build-{self.platform.hostname}" - - @property - def bench_src_path(self) -> pathlib.Path: - return self._src_dir - - @staticmethod - def get_build_var_names() -> List[str]: - return [] - - @staticmethod - def get_run_var_names() -> List[str]: - return ["lock"] - - def clean_bench(self) -> None: - pass - - def prebuild_bench( - self, - **kwargs, - ) -> int: - cmake_configure_build( - platform=self.platform, - src_dir=self.bench_src_path, - build_dir=self._build_dir, - debug=self.must_debug(), - make_suffix=self._parallel_make_str(), - ) - return 0 - - def build_bench( - self, - **kwargs, - ) -> None: - pass - - def single_run( - self, - **kwargs, - ) -> str: - current_dir = self._build_dir - environment = self._preload_env( - **kwargs, - ) - - run_command = ["./mutex"] - - wrapped_run_command, wrapped_environment = self._wrap_command( - run_command=run_command, - environment=environment, - **kwargs, - ) - - output = self.run_bench_command( - run_command=run_command, - wrapped_run_command=wrapped_run_command, - current_dir=current_dir, - environment=environment, - wrapped_environment=wrapped_environment, - print_output=False, - ignore_ret_codes=(1,), - ) - print(output) - return output - - def parse_output_to_results( # pylint: disable=arguments-differ - self, - command_output: str, - **_kwargs, - ) -> Dict[str, Any]: - result_dict = {} - return result_dict + def __init__( + self, + tilt_locks_dir: pathlib.Path, + build_prefix: str = "build", + debug_mode: bool = False, + ) -> None: + super().__init__(src_path=tilt_locks_dir, debug_mode=debug_mode) + self.build_dir = self.src_path / f"{build_prefix}-{self.platform.hostname}" + + def build(self) -> None: + print("[INFO] Building Tilt library...") + cmake_configure_build( + platform=self.platform, + src_dir=self.src_path, + build_dir=self.build_dir, + debug=self._debug_mode, + make_suffix=" -j1", + ) + + def preload( + self, + **kwargs, + ) -> Tuple[LdPreloadLibraries, EnvironmentVariables]: + ld_preloads, env_vars = super().preload( + **kwargs, + ) + + lock = kwargs.get("lock") + if lock: + lib_lockname = f"lib{lock}" + lib_path = (self.build_dir / f"{lib_lockname}.so").resolve() + if lib_path.is_file(): + ld_preloads.append(str(lib_path)) + else: + print( + ( + f"[WARNING] Tilt lib lock with name {lib_lockname} not found " + f"(in '{lib_path}'), not enabling." + ), + file=sys.stderr, + ) + raise ValueError( + f"Tilt lib lock with name {lib_lockname} not found in '{lib_path}'" + ) + + return ld_preloads, env_vars diff --git a/examples/tilt/locks/CMakeLists.txt b/examples/tilt/locks/CMakeLists.txt index f37392bb..d069155b 100644 --- a/examples/tilt/locks/CMakeLists.txt +++ b/examples/tilt/locks/CMakeLists.txt @@ -1,18 +1,35 @@ +# Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +# SPDX-License-Identifier: MIT + cmake_minimum_required(VERSION 3.10) project(tiltlock C) add_subdirectory(${CMAKE_SOURCE_DIR}/../deps/libvsync/ build_libvsync) +add_subdirectory(${CMAKE_SOURCE_DIR}/../deps/reciplock/ build_reciplock) + include_directories(include ${CMAKE_SOURCE_DIR}/../deps/tilt/include/) add_library(taslock SHARED taslock.c) add_library(caslock SHARED caslock.c) add_library(vcaslock-lse SHARED vcaslock.c) add_library(vcaslock-nolse SHARED vcaslock.c) +add_library(clhlock SHARED clhlock.c) +add_library(hemlock SHARED hemlock.c) +add_library(mcslock SHARED mcslock.c) +add_library(ticketlock SHARED ticketlock.c) +add_library(twalock SHARED twalock.c) +add_library(reciplock SHARED reciplock.c) target_link_libraries(vcaslock-lse vsync) target_link_libraries(vcaslock-nolse vsync) +target_link_libraries(clhlock vsync) +target_link_libraries(hemlock vsync) +target_link_libraries(mcslock vsync) +target_link_libraries(ticketlock vsync) +target_link_libraries(twalock vsync) +target_link_libraries(reciplock reciplock_headers) if(CMAKE_SYSTEM_PROCESSOR MATCHES "aarch64" OR CMAKE_SYSTEM_PROCESSOR MATCHES "armv8") target_compile_definitions(vcaslock-nolse PRIVATE VATOMIC_DISABLE_ARM64_LSE) diff --git a/examples/tilt/locks/clhlock.c b/examples/tilt/locks/clhlock.c new file mode 100644 index 00000000..d9b50dfc --- /dev/null +++ b/examples/tilt/locks/clhlock.c @@ -0,0 +1,40 @@ +// Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include + +static __thread clh_node_t context; + +typedef struct tilt_mutex { + clhlock_t lock; +} tilt_mutex_t; + +static void +tilt_mutex_init(tilt_mutex_t *m) +{ + clhlock_init(&m->lock); +} + +static void +tilt_mutex_destroy(tilt_mutex_t *m) +{ +} + +static void +tilt_mutex_lock(tilt_mutex_t *m) +{ + clhlock_acquire(&m->lock, &context); +} + +static void +tilt_mutex_unlock(tilt_mutex_t *m) +{ + clhlock_release(&m->lock, &context); +} + +static bool +tilt_mutex_trylock(tilt_mutex_t *m) +{ +} + diff --git a/examples/tilt/locks/hemlock.c b/examples/tilt/locks/hemlock.c new file mode 100644 index 00000000..b52440f7 --- /dev/null +++ b/examples/tilt/locks/hemlock.c @@ -0,0 +1,41 @@ +// Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include + +static __thread hem_node_t context; + +typedef struct tilt_mutex { + hemlock_t lock; +} tilt_mutex_t; + +static void +tilt_mutex_init(tilt_mutex_t *m) +{ + hemlock_init(&m->lock); +} + +static void +tilt_mutex_destroy(tilt_mutex_t *m) +{ +} + +static void +tilt_mutex_lock(tilt_mutex_t *m) +{ + hemlock_acquire(&m->lock, &context); +} + +static void +tilt_mutex_unlock(tilt_mutex_t *m) +{ + hemlock_release(&m->lock, &context); +} + +static bool +tilt_mutex_trylock(tilt_mutex_t *m) +{ + return hemlock_tryacquire(&m->lock, &context); +} + diff --git a/examples/tilt/locks/mcslock.c b/examples/tilt/locks/mcslock.c new file mode 100644 index 00000000..1ca29352 --- /dev/null +++ b/examples/tilt/locks/mcslock.c @@ -0,0 +1,40 @@ +// Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include + +static __thread mcs_node_t context; + +typedef struct tilt_mutex { + mcslock_t lock; +} tilt_mutex_t; + +static void +tilt_mutex_init(tilt_mutex_t *m) +{ + mcslock_init(&m->lock); +} + +static void +tilt_mutex_destroy(tilt_mutex_t *m) +{ +} + +static void +tilt_mutex_lock(tilt_mutex_t *m) +{ + mcslock_acquire(&m->lock, &context); +} + +static void +tilt_mutex_unlock(tilt_mutex_t *m) +{ + mcslock_release(&m->lock, &context); +} + +static bool +tilt_mutex_trylock(tilt_mutex_t *m) +{ +} + diff --git a/examples/tilt/locks/reciplock.c b/examples/tilt/locks/reciplock.c new file mode 100644 index 00000000..40c85ab5 --- /dev/null +++ b/examples/tilt/locks/reciplock.c @@ -0,0 +1,35 @@ +// Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include + +typedef struct tilt_mutex { + reciplock_t lock; +} tilt_mutex_t; + +static void tilt_mutex_init(tilt_mutex_t *m) +{ + reciplock_init(&m->lock); +} + +static void tilt_mutex_destroy(tilt_mutex_t *m) +{ + +} + +static void tilt_mutex_lock(tilt_mutex_t *m) +{ + reciplock_acquire(&m->lock); +} + +static void +tilt_mutex_unlock(tilt_mutex_t *m) +{ + reciplock_release(&m->lock); +} + +static bool +tilt_mutex_trylock(tilt_mutex_t *m) +{ +} diff --git a/examples/tilt/locks/ticketlock.c b/examples/tilt/locks/ticketlock.c new file mode 100644 index 00000000..00b858a1 --- /dev/null +++ b/examples/tilt/locks/ticketlock.c @@ -0,0 +1,35 @@ +// Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include + +typedef struct tilt_mutex { + ticketlock_t lock; +} tilt_mutex_t; + +static void tilt_mutex_init(tilt_mutex_t *m) +{ + ticketlock_init(&m->lock); +} + +static void tilt_mutex_destroy(tilt_mutex_t *m) +{ +} + +static void tilt_mutex_lock(tilt_mutex_t *m) +{ + ticketlock_acquire(&m->lock); +} + +static void +tilt_mutex_unlock(tilt_mutex_t *m) +{ + ticketlock_release(&m->lock); +} + +static bool +tilt_mutex_trylock(tilt_mutex_t *m) +{ + return ticketlock_tryacquire(&m->lock); +} diff --git a/examples/tilt/locks/twalock.c b/examples/tilt/locks/twalock.c new file mode 100644 index 00000000..06fd2a78 --- /dev/null +++ b/examples/tilt/locks/twalock.c @@ -0,0 +1,38 @@ +// Copyright (C) 2025 Vrije Universiteit Brussel. All rights reserved. +// SPDX-License-Identifier: MIT + +#include +#include + +TWALOCK_ARRAY_DECL; +twalock_t g_lock = TWALOCK_INIT(); + +typedef struct tilt_mutex { + twalock_t lock; +} tilt_mutex_t; + +static void tilt_mutex_init(tilt_mutex_t *m) +{ + twalock_init(&m->lock); +} + +static void tilt_mutex_destroy(tilt_mutex_t *m) +{ +} + +static void tilt_mutex_lock(tilt_mutex_t *m) +{ + twalock_acquire(&m->lock); +} + +static void +tilt_mutex_unlock(tilt_mutex_t *m) +{ + twalock_release(&m->lock); +} + +static bool +tilt_mutex_trylock(tilt_mutex_t *m) +{ + return twalock_tryacquire(&m->lock); +} diff --git a/tutorials/leveldb-bench/campaign_leveldb_locks.py b/tutorials/leveldb-bench/campaign_leveldb_locks.py index 0ce97084..2439d69c 100755 --- a/tutorials/leveldb-bench/campaign_leveldb_locks.py +++ b/tutorials/leveldb-bench/campaign_leveldb_locks.py @@ -119,6 +119,47 @@ def get_vcaslock_lse_prefetch_campaign(build_path: Path) -> CampaignCartesianPro shared_libs=[PrecompiledSharedLib(path=vcaslocklib_path, env_vars=None)], ) +def get_clhlock_campaign(build_path: Path) -> CampaignCartesianProduct: + clhlocklib_path = (build_path / "libclhlock.so").resolve() + return get_campaign( + mutex_constant="CLH lock", + shared_libs=[PrecompiledSharedLib(path=clhlocklib_path, env_vars=None)], + ) + +def get_hemlock_campaign(build_path: Path) -> CampaignCartesianProduct: + hemlocklib_path = (build_path / "libhemlock.so").resolve() + return get_campaign( + mutex_constant="Hemlock", + shared_libs=[PrecompiledSharedLib(path=hemlocklib_path, env_vars=None)], + ) + +def get_mcslock_campaign(build_path: Path) -> CampaignCartesianProduct: + mcslocklib_path = (build_path / "libmcslock.so").resolve() + return get_campaign( + mutex_constant="MCS lock", + shared_libs=[PrecompiledSharedLib(path=mcslocklib_path, env_vars=None)], + ) + +def get_reciplock_campaign(build_path: Path) -> CampaignCartesianProduct: + reciplocklib_path = (build_path / "libreciplock.so").resolve() + return get_campaign( + mutex_constant="Reciprocating lock", + shared_libs=[PrecompiledSharedLib(path=reciplocklib_path, env_vars=None)], + ) + +def get_ticketlock_campaign(build_path: Path) -> CampaignCartesianProduct: + ticketlocklib_path = (build_path / "libticketlock.so").resolve() + return get_campaign( + mutex_constant="Ticketlock", + shared_libs=[PrecompiledSharedLib(path=ticketlocklib_path, env_vars=None)], + ) + +def get_twalock_campaign(build_path: Path) -> CampaignCartesianProduct: + twalocklib_path = (build_path / "libtwalock.so").resolve() + return get_campaign( + mutex_constant="TWA lock", + shared_libs=[PrecompiledSharedLib(path=twalocklib_path, env_vars=None)], + ) def main() -> None: platform = get_current_platform() @@ -132,6 +173,12 @@ def main() -> None: get_vcaslock_lse_campaign(build_path=build_ok), get_vcaslock_nolse_prefetch_campaign(build_path=build_regression), get_vcaslock_lse_prefetch_campaign(build_path=build_regression), + # get_clhlock_campaign(build_path=build_ok), # TODO: fix Tilt implementation + get_hemlock_campaign(build_path=build_ok), + get_mcslock_campaign(build_path=build_ok), + get_reciplock_campaign(build_path=build_ok), + get_ticketlock_campaign(build_path=build_ok), + get_twalock_campaign(build_path=build_ok), ] suite = CampaignSuite(campaigns=campaigns) suite.print_durations()