diff options
Diffstat (limited to 'content')
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/.clang-format | 15 | ||||
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/.gitignore | 3 | ||||
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/Makefile | 37 | ||||
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/a64-basic-llsc.cc | 89 | ||||
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/a64-llsc.cc | 176 | ||||
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/cas-aba-issue.cc | 87 | ||||
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/cas-ptr-generation.cc | 134 | ||||
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/index.md | 670 | ||||
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/list-abc.drawio | 61 | ||||
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/list-abc.svg | 4 | ||||
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/list-ac.drawio | 58 | ||||
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/list-ac.svg | 4 | ||||
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/list-b.drawio | 55 | ||||
-rw-r--r-- | content/2023-09-01-cas-llsc-aba/list-b.svg | 4 |
14 files changed, 1397 insertions, 0 deletions
diff --git a/content/2023-09-01-cas-llsc-aba/.clang-format b/content/2023-09-01-cas-llsc-aba/.clang-format new file mode 100644 index 0000000..1c7765b --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/.clang-format @@ -0,0 +1,15 @@ +# dotfiles -- clang-format +# author: johannst +# doc : https://clang.llvm.org/docs/ClangFormatStyleOptions.html +--- + +Language: Cpp +BasedOnStyle: Chromium + +AllowShortFunctionsOnASingleLine: None + +AccessModifierOffset: -2 +IndentWidth: 2 + +AlignConsecutiveMacros: true +AlignConsecutiveAssignments: true diff --git a/content/2023-09-01-cas-llsc-aba/.gitignore b/content/2023-09-01-cas-llsc-aba/.gitignore new file mode 100644 index 0000000..94b26cc --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/.gitignore @@ -0,0 +1,3 @@ +*.elf +.$*.drawio.dtmp +.$*.drawio.bkp diff --git a/content/2023-09-01-cas-llsc-aba/Makefile b/content/2023-09-01-cas-llsc-aba/Makefile new file mode 100644 index 0000000..602418f --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/Makefile @@ -0,0 +1,37 @@ +BINS = cas-aba-issue cas-ptr-generation a64-basic-llsc a64-llsc + +CFLAGS_cas-ptr-generation = -mcx16 +CFLAGS = -g -O3 -Wall -Wextra $(CFLAGS_$(*)) + +A64_SYSROOT = /usr/aarch64-linux-gnu + +run: $(BINS:%=run-%) +build: $(BINS:%=%.elf) + +# -- HOST NATIVE TARGETS ------------------------------------------------------- + +run-%: %.elf + ./$^ + +%.elf : %.cc + $(CXX) $(CFLAGS) -o $@ $^ -latomic + +disasm: cas-ptr-generation.elf + objdump -C -d -j .text -M intel --no-show-raw-insn --visualize-jumps=color $^ | less -R + +# -- ARM64 CROSS TARGETS ------------------------------------------------------- + +a64-%.elf: a64-%.cc + clang++ $(CFLAGS) -target aarch64-linux-gnu --sysroot=$(A64_SYSROOT) -fuse-ld=lld -o $@ $^ + #llvm-objdump -j .text -C -d $@ + +run-a64-%: a64-%.elf + qemu-aarch64 -L $(A64_SYSROOT) -E LD_LIBRARY_PATH=$(A64_SYSROOT)/lib64 $^ + +# -- MISC TARGETS -------------------------------------------------------------- + +fmt: + clang-format -i *.cc + +clean: + $(RM) *.elf diff --git a/content/2023-09-01-cas-llsc-aba/a64-basic-llsc.cc b/content/2023-09-01-cas-llsc-aba/a64-basic-llsc.cc new file mode 100644 index 0000000..82c68e1 --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/a64-basic-llsc.cc @@ -0,0 +1,89 @@ +#include <cassert> +#include <cstdint> +#include <cstdio> +#include <thread> + +#ifndef __aarch64__ +#error "This must be compiled for arm64!" +#endif + +// NOTES on the inline assembly: +// +// * AArch64 constraint. +// https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html +// +// Q: A memory address which uses a single base register with no offset. +// +// * Output constraint. +// https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Output-Operands +// +// Use the '&' constraint modifier on all output operands that must not +// overlap an input. Otherwise, GCC may allocate the output operand in the +// same register as an unrelated input operand, on the assumption that the +// assembler code consumes its inputs before producing outputs. This +// assumption may be false if the assembler code actually consists of more +// than one instruction. + +// LDXR: Load exclusive register wrapper. +// +// Read from ADDR and marked address for exclusive access (exclusive monitor). +// +// Return value read from memory. +// +// NOTE: No memory ordering semantics. +// +// https://developer.arm.com/documentation/ddi0596/latest/Base-Instructions/LDXR--Load-Exclusive-Register-?lang=en +inline uint64_t ldxr(uint64_t* addr) { + uint64_t ret; + asm volatile("ldxr %0, %1" : "=r"(ret) : "Q"(*addr) : "memory"); + return ret; +} + +// STXR: Store exclusive register wrapper. +// +// Conditionally write VAL to ADDR if ADDR is marked for exclusive access by a +// previous exclusive load (eg LDXR). +// +// Return 0 if the write was successful, 1 otherwise. +// +// NOTE: No memory ordering semantics. +// +// https://developer.arm.com/documentation/ddi0596/latest/Base-Instructions/STXR--Store-Exclusive-Register-?lang=en +inline bool stxr(uint64_t* addr, uint64_t val) { + uint32_t ret; + asm volatile("stxr %w0, %2, %1" + : "=&r"(ret), "=Q"(*addr) + : "r"(val) + : "memory"); + return ret == 0; +} + +int main() { + uint64_t mem = 42; + + auto T1 = std::thread([&mem]() { + // Write to exclusive location (does clear exclusive monitor). + mem = 2222; + // Full memory barrier. + __sync_synchronize(); + }); + + uint64_t old = ldxr(&mem); + + // Some artificial delay w/o an explicit context switch (eg syscall) as that + // would clear the exclusive monitor, though it can still be interupted by + // the scheduler. + // Delay is "tuned" for my ARM silicon. + for (int i = 0; i < (1 << 13); ++i) { + asm volatile("nop"); + } + + // Full memory barrier. + __sync_synchronize(); + + bool ok = stxr(&mem, 1111); + printf("old: %lu -> mem: %lu | ok: %d\n", old, mem, ok); + + T1.join(); + return ok ? 0 : 1; +} diff --git a/content/2023-09-01-cas-llsc-aba/a64-llsc.cc b/content/2023-09-01-cas-llsc-aba/a64-llsc.cc new file mode 100644 index 0000000..37563b5 --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/a64-llsc.cc @@ -0,0 +1,176 @@ +#include <cassert> +#include <cstdint> +#include <cstdio> + +#include <atomic> +#include <thread> + +#include <unistd.h> + +#ifndef __aarch64__ +#error "This must be compiled for arm64!" +#endif + +// LDXR: Load exclusive register wrapper. +// +// Read from ADDR and marked address for exclusive access (exclusive monitor). +// +// Return value read from memory. +// +// NOTE: No memory ordering semantics. +// +// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/LDXR--Load-Exclusive-Register-?lang=en +uint64_t ldxr(uint64_t* addr) { + uint64_t ret; + asm volatile("ldxr %0, %1" : "=r"(ret) : "Q"(*addr) : "memory"); + return ret; +} + +// STXR: Store exclusive register wrapper. +// +// Conditionally write VAL to ADDR if ADDR is marked for exclusive access by a +// previous exclusive load (eg LDXR). +// +// Return 0 if the write was successful, 1 otherwise. +// +// NOTE: No memory ordering semantics. +// +// https://developer.arm.com/documentation/ddi0596/2021-12/Base-Instructions/STXR--Store-Exclusive-Register-?lang=en +bool stxr(uint64_t* addr, uint64_t val) { + uint32_t ret; + asm volatile("stxr %w0, %2, %1" + : "=&r"(ret), "=Q"(*addr) + : "r"(val) + : "memory"); + return ret == 0; +} + +inline void mem_barrier() { + asm volatile("dmb ish" : /* out */ : /* in */ : "memory"); +} + +// -- BLOCK STACK -------------------------------------------------------------- + +struct BlockStack { + struct Block { + Block* next{nullptr}; + unsigned mem{0}; + }; + + void push(Block* blk) { + printf("[%d] push blk->mem=%d\n", gettid(), blk->mem); + + do { + blk->next = load_head_ex(); + } while (!store_head_ex(blk)); + } + + Block* pop(bool delay = false) { + Block *blk, *next; + do { + blk = load_head_ex(); + next = blk->next; + + // Spin some time to give other thread a chance to replace head. + // Delay somewhat tuned for my ARM device. + for (int i = 0; delay && (i < (1 << 16)); ++i) { + asm volatile("nop"); + } + delay = false; + } while (!store_head_ex(next)); + + printf("[%d] pop blk->mem=%d\n", gettid(), blk->mem); + return blk; + } + +private: + Block* load_head_ex() const { + static_assert(sizeof(void*) == sizeof(uint64_t), + "Pointer size miss-match!"); + + Block* blk = (Block*)ldxr((uint64_t*)&m_head); + mem_barrier(); + return blk; + } + + bool store_head_ex(Block* blk) { + static_assert(sizeof(void*) == sizeof(uint64_t), + "Pointer size miss-match!"); + + const bool success = stxr((uint64_t*)&m_head, (uint64_t)blk); + mem_barrier(); + return success; + } + + Block* m_head{nullptr}; +}; + +int main() { + BlockStack::Block B1, B2, B3; + B1.mem = 1; + B2.mem = 2; + B3.mem = 3; + + // Create free memory block list. + // S: head -> 1 -> 2 -> 3. + BlockStack S; + S.push(&B3); + S.push(&B2); + S.push(&B1); + + // Atomics to coordinate when threads are being released. + std::atomic<size_t> worker(0); + std::atomic<bool> release(false); + + auto T1 = std::thread([&S, &worker, &release]() { + // Notify thread T1 ready, and wait for release. + worker.fetch_add(1); + while (!release.load()) {} + + // Read head=1 & next=2, and add some artificial delay by executing NOPs. + // This gives T2 some time to pop 1 & 2 and push back 1, which triggered the + // ABA problem with the CAS instruction. + // + // However with the LL/SC atomics, our exclusive monitor for T1 after the + // exclusive load of the head is cleared after T2 updated the head. + // Therefore the exclusive store after the NOP delay will fail and we will + // load the new head pointer, circumventing the ABA problem. + // + // NOTE: This does not work under qemu user emulation (aarch64) as qemu does + // not implement the strong LL/SC semantics. + // https://elixir.bootlin.com/qemu/v8.1.2/source/target/arm/tcg/translate-a64.c#L2411 + auto* B1 = S.pop(true /* nops delay */); + assert(B1->mem == 1); + // Pops 3, correct, no ABA problem. + auto* B2 = S.pop(); + assert(B2->mem == 3); + }); + + auto T2 = std::thread([&S, &worker, &release]() { + // Notify thread T2 ready, and wait for release. + worker.fetch_add(1); + while (!release.load()) {} + + // Artificial sleep, such that T1 entered pop and read head & next. + // Delay somewhat tuned for my ARM device. + for (int i = 0; i < (1<<8); ++i) { + asm volatile("nop"); + } + + // Pop 1 & 2. + auto* B1 = S.pop(); + assert(B1->mem == 1); + auto* B2 = S.pop(); + assert(B2->mem == 2); + + // Re-insert 1. + S.push(B1); + }); + + // Wait T1 & T2 ready, then release both threads. + while (worker.load() != 2) {} + release.store(true); + + T2.join(); + T1.join(); +} diff --git a/content/2023-09-01-cas-llsc-aba/cas-aba-issue.cc b/content/2023-09-01-cas-llsc-aba/cas-aba-issue.cc new file mode 100644 index 0000000..2482781 --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/cas-aba-issue.cc @@ -0,0 +1,87 @@ +#include <cassert> +#include <cstdio> + +#include <atomic> +#include <thread> + +#include <unistd.h> + +// -- BLOCK STACK -------------------------------------------------------------- + +struct BlockStack { + struct Block { + Block* next{nullptr}; + unsigned mem{0}; + }; + + void push(Block* blk) { + printf("[%d] push blk->mem=%d\n", gettid(), blk->mem); + + do { + blk->next = m_head; + } while (!m_head.compare_exchange_weak(blk->next, blk)); + } + + Block* pop(int s = 0) { + Block *blk, *next; + do { + blk = m_head; + next = blk->next; + // Simulate thread being interrupted. + if (s) { + sleep(s); + } + } while (!m_head.compare_exchange_weak(blk, next)); + + printf("[%d] pop blk->mem=%d\n", gettid(), blk->mem); + return blk; + } + +private: + std::atomic<Block*> m_head{nullptr}; +}; + +// -- MAIN --------------------------------------------------------------------- + +int main() { + BlockStack::Block B1, B2, B3; + B1.mem = 1; + B2.mem = 2; + B3.mem = 3; + + // Create free memory block list. + // S: head -> 1 -> 2 -> 3. + BlockStack S; + S.push(&B3); + S.push(&B2); + S.push(&B1); + + auto T1 = std::thread([&S]() { + // Read head=1 & next=2, then get interrupted (simulated by sleep). + // After resuming, T2 already popped 1 & 2 and pushed again 1 (ABA). + // The CAS will set head pointing to 2 and pop block 1. Block 2 is inserted + // again into the free list, even it is not free. + auto* B1 = S.pop(2); + assert(B1->mem == 1); + // Pops 2, which is currently owned by T2, double usage (broken). + auto* B2 = S.pop(); + assert(B2->mem == 2); + }); + + auto T2 = std::thread([&S]() { + // Artificial sleep, such that T1 entered pop and read head & next. + sleep(1); + + // Pop 1 & 2. + auto* B1 = S.pop(); + assert(B1->mem == 1); + auto* B2 = S.pop(); + assert(B2->mem == 2); + + // Re-insert 1. + S.push(B1); + }); + + T2.join(); + T1.join(); +} diff --git a/content/2023-09-01-cas-llsc-aba/cas-ptr-generation.cc b/content/2023-09-01-cas-llsc-aba/cas-ptr-generation.cc new file mode 100644 index 0000000..319481b --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/cas-ptr-generation.cc @@ -0,0 +1,134 @@ +#include <cassert> +#include <cstdint> +#include <cstdio> + +#include <atomic> +#include <thread> + +#include <unistd.h> + +#if __x86_64 != 1 +#error "Example only for x86_64" +#endif + +// Only used because we look at the disassembly. +#define NOINLINE __attribute__((noinline)) + +// -- UTILITIES ---------------------------------------------------------------- + +template <typename T> +NOINLINE bool cmpxchg16b(T* dest, T oldval, T newval) { + static_assert(sizeof(T) == 16, "Required for CMPXCHG16B"); + static_assert(alignof(T) == 16, "Required for CMPXCHG16B"); + + const auto as_u128 = [](T* ptr) { + return reinterpret_cast<unsigned __int128*>(ptr); + }; + // Use legacy __sync atomic builtin instead __atomic builtin because gcc will + // directly emit the 16byte compare exchange instruction instead of emitting a + // library call. This is nice when looking at the disassembly, weeh. + // + // -mcx16 + // This option enables GCC to generate "CMPXCHG16B" instructions in 64-bit + // code to implement compare-and- exchange operations on 16-byte aligned + // 128-bit objects. This is useful for atomic updates of data + // structures exceeding one machine word in size. The compiler uses this + // instruction to implement __sync Builtins. However, for __atomic Builtins + // operating on 128-bit integers, a library call is always used. + return __sync_bool_compare_and_swap(as_u128(dest), *as_u128(&oldval), + *as_u128(&newval)); +} + +// -- BLOCK STACK -------------------------------------------------------------- + +struct BlockStack { + struct Block { + Block* next{nullptr}; + unsigned mem{0}; + }; + + void push(Block* blk) { + printf("[%d] push blk->mem=%d\n", gettid(), blk->mem); + + GenerationPtr old_head, new_head; + do { + old_head = m_head; + blk->next = old_head.ptr; + new_head.ptr = blk; + new_head.gen = old_head.gen + 1; + } while (!cmpxchg16b(&m_head, old_head, new_head)); + } + + Block* pop(int s = 0) { + GenerationPtr old_head, new_head; + do { + old_head = m_head; + new_head.ptr = old_head.ptr->next; + new_head.gen = old_head.gen + 1; + + // Simulate thread being interrupted. + if (s) { + sleep(s); + } + } while (!cmpxchg16b(&m_head, old_head, new_head)); + + printf("[%d] pop blk->mem=%d\n", gettid(), old_head.ptr->mem); + return old_head.ptr; + } + +private: + struct GenerationPtr { + Block* ptr{nullptr}; + uint64_t gen{0}; + } __attribute__((aligned(16))); + + GenerationPtr m_head; +}; + +// -- MAIN --------------------------------------------------------------------- + +int main() { + BlockStack::Block B1, B2, B3; + B1.mem = 1; + B2.mem = 2; + B3.mem = 3; + + // Create free memory block list. + // S: head -> 1 -> 2 -> 3. + BlockStack S; + S.push(&B3); + S.push(&B2); + S.push(&B1); + + auto T1 = std::thread([&S]() { + // Read head=1 & next=2, then get interrupted (simulated by sleep). + // After resuming, T2 already popped 1 & 2 and pushed again 1 (ABA). + // + // The first CAS will fail even block 1 is currently at the beginning of the + // list as the generation of the OLD_ROOT does not match the generation of + // M_ROOT. The CAS loop will retry, read the new M_ROOT, and hence the new + // NEXT pointer pointing to 3 and then successfully update M_ROOT. + auto* B1 = S.pop(2); + assert(B1->mem == 1); + // Pops 3, as it should be. + auto* B3 = S.pop(); + assert(B3->mem == 3); + }); + + auto T2 = std::thread([&S]() { + // Artificial sleep, such that T1 entered pop and read head & next. + sleep(1); + + // Pop 1 & 2. + auto* B1 = S.pop(); + assert(B1->mem == 1); + auto* B2 = S.pop(); + assert(B2->mem == 2); + + // Re-insert 1. + S.push(B1); + }); + + T2.join(); + T1.join(); +} diff --git a/content/2023-09-01-cas-llsc-aba/index.md b/content/2023-09-01-cas-llsc-aba/index.md new file mode 100644 index 0000000..177404f --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/index.md @@ -0,0 +1,670 @@ ++++ +title = "CAS, ABA and LL/SC" + +[taxonomies] +tags = ["threading", "linux", "arm", "x86", "qemu"] ++++ + +If this zoo of abbreviations does not mean anything to you then "[fly you +fools][fly-you-fools]" as long as you still can :^) + +Jokes aside. I recently had the opportunity to ramp up a new co-worker on the +different semantics of `atomic` instructions (compare and swap `CAS` vs +load-linked store-conditional `LL/SC`), `lock-free` programming and the `ABA` +problem. + +In the realm of `lock-free` programming and the `ABA` problem, I shared a war +story from some years ago when I was debugging a random memory corruption in +our multi-threaded simulator. Sharing that story actually gave the idea to +write this post as reference for potential new co-workers or all the internet +people <3. + +After many hours of debugging the random memory corruption, which *obviously* +did not occur very often, it turned out to be a bug in a custom memory +allocator. To be more precise, in a primitive used by the allocator to manage a +list of free memory blocks. The allocator was used from multiple threads +concurrently and hence `pushing` and `popping` free memory blocks had to be +thread-safe. + +The operations to manage the free block list were implemented with an +`lock-free` algorithm, which we will visit later when analyzing the bug. +Before, let us revisit the fundamental semantics of atomic `compare-and-swap +(CAS)` operations, such that everyone has the same understanding. If you are +already familiar with CAS, feel free to skip ahead to the exploration of the +[lock-free memory block primitive](#lock-free-free-memory-block-primitive). + +## compare-and-swap (CAS) + +The following pseudo code gives a good mental model for an atomic CAS +operation. The described compare-and-swap is executed atomically without giving +any other execution stream the chance to interfere in the middle of this +operation. +```cpp +// bool cas(T* val, T* expected, T desired); +// +// Does the following operation atomically. +// +bool is_expected = (*val == *expected); +if (is_expected) { + *val = desired; +} else { + *expected = *val; +} +return is_expected; +``` + +Below are some concrete examples using the cpp [`std::atomic`][cpp-std-atomic] +type. +```cpp +// bool std::atomic<T>::compare_exchange_weak(T& expected, T desired); + +std::atomic<int> val(42); +int expected = 43; +// val != expected -> expected = val +assert(val.compare_exchange_weak(expected, 1337) == false); +// val == expected -> val = desired +assert(val.compare_exchange_weak(expected, 1337) == true); +assert(val.load() == 1337); +``` + +When talking about atomics we also need to talk about [memory +ordering][cpp-mem-order]. + +Memory ordering concerns the ordering of memory accesses, both atomic and +non-atomic, in relation to atomic operations. It also determines how +side-effects, such as unrelated writes for example, are observed across +threads. Essentially, memory ordering sets the rules for optimizers to reorder +instructions and dictates compilers to insert memory barrier instructions, +ensuring the memory ordering guarantees, in cases where the underlying +hardware's memory model is relatively weak and subject to instruction +reordering. + +For the purpose of our discussion, we'll focus on the concept of `sequential +consistent` ordering, as it is the most intuitive and also the default in C++ +when utilizing std::atomic. When dealing with atomic loads and stores, it +provides the following guarantees: +1. `atomic store`: On the current thread no read / write preceding the *store* + can be reordered after the *store*. Other threads will observe all writes + before they observe the atomic store. +1. `atomic load`: On the current thread no read / write following the *load* + can be reordered before the *load*. The current thread observes all writes + done on other threads, which happen before the store to the atomic. + +In the example below that means, on *Thread 1* the `M = X` can not be reordered +after the `A.store(1)`. When *Thread 2* sees the atomic variable change, it is +guaranteed that is observes the write to M on *Thread 1* and hence `print(M)` +would give X. +``` +Thread 1 Thread 2 + +M = X if (A.load() == 1) +A.store(1) print(M) +``` + +An atomic CAS operations performs and atomic load and store, which means that +no read or write can be reordered across the CAS operation in any direction. + +With that, we just scratched the surface of the rabbit hole, but sufficient for +the remaining discussion. The 2017 cppcon talk from [Fedor Pikus "C++ atomics, +from basic to advanced. What do they really do?"][yt-atomics] goes into more +depth and is highly recommended. I also added some more +[references](#references) at the end of the post. + +## `lock-free` free memory block primitive + +The code below gives the essence of the primitive to manage the free memory +blocks with the goal of being thread-safe. Essentially it is a linked list of +`BlockData::Block` objects where the head pointer to the beginning of the list +is swapped atomically. + +Details such as how the list initially is filled up or how it grows +dynamically at runtime are left out here. The same goes for any sort of error +handling as none of this is relevant for the discussion. + +```cpp +struct BlockStack { + struct Block { + Block* next{nullptr}; + // .. + }; + + void push(Block* blk) { + do { + blk->next = m_head; + } while (!m_head.compare_exchange_weak(blk->next, blk)); + } + + Block* pop() { + Block* blk; + do { + blk = m_head; + // eg, if (blk == nullptr) expand with new free Blocks. + } while (!m_head.compare_exchange_weak(blk, blk->next)); + return blk; + } + +private: + std::atomic<Block*> m_head{nullptr}; +}; +``` +> The `std::atomic<T>::compare_exchange_weak` overload used has an additional +> argument for the memory ordering with the default value of sequential +> consistent. This ensures in the push() method, that when another thread +> observes the new head pointer the write to *blk->next* before the CAS is also +> visible. + +Taking a first look at the `push()` and `pop()` methods, the implementation +looks quite sound. + +`push()` inserts the `blk` object at the beginning of the list. That means the +newly inserted block needs to point to the current list head and the list head +pointer must be updated to point to the newly inserted block. The head pointer +is swapped atomically, which only succeeds if no one else has updated the head +pointer in the meantime and it still points to `blk->next`. In case the CAS was +not successful, the insert operation is retried in a loop until the block is +inserted. + +`pop()` on the other hand removes the block at the beginning of the list and +sets the second block as the new beginning of the list. This is done by +updating the head pointer to point to the second block entry in the list. +Similar to push, the current head is read once and the head pointer swapped +atomically. This operation is again repeated in a loop until the CAS is +successful. + +However the `pop()` method has a subtle difference compared to the `push()` +method as it dereferences the current head block, which makes it vulnerable to +the `ABA problem`. We will see in a moment what the ABA problem is and why it +is called like that, but first let us slightly re-write the pop method to make +it easier to spot the bug. + +```cpp, linenos +Block* pop() { + Block* blk; + Block* next; + do { + blk = m_head; + next = blk->next; // Deref "our" head block. + } while (!m_head.compare_exchange_weak(blk, next)); + return blk; +} +``` + +Now let us assume we have two threads *Thread 1* and *Thread 2*. Both threads +access the shared `BlockData` instance which currently contains three free +blocks (A), (B) and (C). + +<img src="list-abc.svg"> + +*Thread 1* calls pop() and gets interrupted between line 6 and 7. +- `blk` points to (A) +- `next` points to (B) + +In the meantime *Thread 2* performs the following actions +- pop() -> (A) +- pop() -> (B) +- push((A)) + +After this, *Thread 2* owns block (B) and the `BlockData` instance contains the +free blocks (A) and (C). + +<img src="list-ac.svg"> + +Next, *Thread 1* is resumed and continues with the atomic swap of `m_head`. The +swap will succeed as `m_head (A) == blk (A)` is true, however this operation +will re-insert block (B) as free block since *Thread 1* read `next = (B)` +before being interrupted. + +This leaves us with the following state after *Thread 1* returns from the pop +operation. + +<img src="list-b.svg"> + +We can easily see that this state is catastrophic, as the next pop operation +will return block (B) which is currently used and owned by *Thread 2*. +Additionally, block (C) and the rest of the list is leaked. + +The example above describes the `ABA problem`. Which is, the "inner" state of +the block list is altered, by removing (B), but the observable "outer" state is +unchanged, by removing and re-inserting (A) again, hence ABA. + +The following program [cas-aba-issue.cc](cas-aba-issue.cc) demonstrates the +above described issue and can be used for further experiments. It can be +compiled as follows. +``` +clang++ -o cas-aba-issue cas-aba-issue.cc -latomic +``` + +So, how can the current implementation be fixed? + +The obvious approach would be to rewrite the push() and pop() methods to use a +`lock`. + +However, the point of this post is to look at the `lock-free` approaches and +learn about the different atomic instructions. +Therefore, we will discuss two approaches which require specific instructions, +and therefore are only applicable if the underlying hardware supports that +instructions. +1. Generational pointers using `double-word CAS`. +2. `Exclusive` accesses using load-linked store-conditional. + +## Generational pointers + +In this approach the raw head pointer is replaced with an head object that +holds the raw pointer as well as a counter (the `generation`). The idea being, +when ever the head object is swapped the generation is increased. In practice, +this makes it resilient against the ambiguous raw pointer in the ABA problem, +as in the example discussed above, the head objects would not compare equal due +to a different generation. +> Theoretically, a case can be constructed where the ABA problem occurs, if the +> counter overflows, but that is not really relevant in practice. + +For the following discussion we assume that we are in a x86_64 environment. + +The `GenerationPtr` will be our new head object, holding the raw pointer as +well as the generation counter. +```cpp +struct GenerationPtr { + Block* ptr{nullptr}; + uint64_t gen{0}; +}; +``` + +To be able to atomically compare and swap such an object, the host must support +a double-word CAS, and in this specific case a 16 byte CAS. Most x86_64 hosts +support the [`cmpxchg16b`][cmpxchg16b] instruction which allows to atomically +compare and swap 16 byte when combined with the `lock` prefix. + +We create ourselves a helper function for the 16 byte CAS based on the legacy +`__sync_bool_compare_and_swap` builtin, as both gcc and clang directly emitted +cmpxchg16b instructions instead of library calls with the `-mcx16` target +option enabled. It is irrelevant for this discussion and makes looking at the +disassembly easier. +```cpp +// bool __sync_bool_compare_and_swap (type *ptr, type oldval, type newval); + +template <typename T> +bool cmpxchg16b(T* dest, T oldval, T newval) { + static_assert(sizeof(T) == 16, "Required for cmpxchg16b"); + static_assert(alignof(T) == 16, "Required for cmpxchg16b"); + + const auto as_u128 = [](T* ptr) { + return reinterpret_cast<unsigned __int128*>(ptr); + }; + return __sync_bool_compare_and_swap(as_u128(dest), *as_u128(&oldval), + *as_u128(&newval)); +} +``` + +The codegen with clang 15.0.7 for this function gives the following result on +my machine. + +```asm +; generated by clang version 15.0.7 with -mcx16 + +00000000000016e0 <bool cmpxchg16b<..>(BlockStack::GenerationPtr* dest, + BlockStack::GenerationPtr oldval, + BlockStack::GenerationPtr newval)>: + ; Per SystemV ABI + ; rdi = dest ptr + ; rsi = oldval.ptr + ; rdx = oldval.gen + ; rcx = newval.ptr + ; r8 = newval.gen + 16e0: push rbx + 16e1: mov rbx,rcx ; shuffle args for cmpxchg16b + 16e4: mov rax,rsi ; shuffle args for cmpxchg16b + 16e7: mov rcx,r8 ; shuffle args for cmpxchg16b + ; CMPXCHG16B + ; compare rdx:rax (oldval) with [rdi] (dest*) + ; if equal then load rcx:rbx (newval) into [rdi] (dest*) + 16ea: lock cmpxchg16b OWORD PTR [rdi] + 16ef: sete al + 16f2: pop rbx + 16f3: ret +``` + +With that we can update the type of `BlockStack::m_head` to GenerationPtr and +adapt the push() and pop() methods. + +```cpp, linenos +// BlockStack .. + +void push(Block* blk) { + GenerationPtr old_head, new_head; + do { + old_head = m_head; + blk->next = old_head.ptr; + new_head.ptr = blk; + new_head.gen = old_head.gen + 1; + } while (!cmpxchg16b(&m_head, old_head, new_head)); +} + +Block* pop() { + GenerationPtr old_head, new_head; + do { + old_head = m_head; + new_head.ptr = old_head.ptr->next; + new_head.gen = old_head.gen + 1; + } while (!cmpxchg16b(&m_head, old_head, new_head)); + return old_head.ptr; +} +``` +> For the purpose of this discussion we assume that the +> __sync_bool_compare_and_swap builtin acts as full barrier. In practice the +> __sync builtins should not be used anymore, and instead the newer __atomic +> builtins which allow to specify the memory ordering. + +A note about the loads of m_head in line 6 and line 16. Those loads are not +atomic and the value of m_head could be updated from another thread in the +midst when loading the values. However if we read an inconsistent m_head the +CAS would fail and we would retry our operation. + +The following program [cas-ptr-generation.cc](cas-ptr-generation.cc) provides +the full example. When being run it demonstrates how the _generation pointer_ +prevents the ABA problem. It can be compiled as follows. +``` +clang++ -o cas-ptr-generation cas-ptr-generation.cc -latomic -mcx16 +``` + +## Load linked store conditional + +So far we looked at atomic CAS instructions which have read-modify-write +semantics and are typically found with CISC ISAs. RISC ISAs on the other hand +usually provide load-linked (LL) store-conditional (SC) instruction pairs to +implement atomic accesses. Compared to the compare-and-swap instruction, LL/SC +are two distinct instructions. The LL instruction loads the current value and +marks the memory region for exclusive access, and the SC instruction only +succeeds and updates the memory location if the memory location is still marked +as exclusive at the time of the store-conditional. If there was an update to +the memory location between the LL/SC pair, the exclusive marker is removed. + +The pseudo code below gives an example of how atomic operations are typically +implemented with LL/SC instructions. The example shows an atomic increment of a +location in memory. +```c +/// Atomically increment *MEM and return OLD value. +uint64_t atomic_fetch_inc(uint64_t* mem) { + uint64_t old; + bool ok; + + do { + old = ll(mem); // Load current value and mark for ex access. + ok = sc(mem, old + 1); // Try to store incremented value. + } while (!ok); + + return old; +} +``` + +For the remaining discussion we will focus on the ARM architecture, and use the +[A64 instruction set][a64-insn] as I have some ARM silicon at home to run the +examples on. + +We can take a look into the [ARM ARM (Architecture Reference +Manual)][arm-arm-a] for the A-profile to find the description of the LL/SC +instructions in the chapter *B2.9 Synchronization and semaphores*. Where +`Load-Exclusive` refers to load-linked and `Store-Exclusive` refers to +store-conditional. +> The model for the use of a Load-Exclusive/Store-Exclusive instruction pair +> accessing a non-aborting memory address x is: +> - The Load-Exclusive instruction reads a value from memory address x. +> - The corresponding Store-Exclusive instruction succeeds in writing back to +> memory address x only if no other observer, process, or thread has +> performed a more recent store to address x. The Store-Exclusive instruction +> returns a status bit that indicates whether the Write Memory succeeded. + +The A64 instruction set supports multiple LL/SC instruction pairs for different +operand sizes and with different memory ordering semantics. We will make use of +the [LDXR][a64-ldxr] and [STXR][a64-stxr] instructions, which are exclusive +load from register and store to register instructions without any memory +ordering semantics. We will use explicit `memory barriers` rather than +instruction pairs with `release-acquire` memory ordering semantics, as this +post is not aimed to discuss the different memory orderings, but give an +overview of different atomic instructions. + +Table *B2-3 Synchronization primitives and associated instruction, A64 +instruction set* in the ARM ARM has a full overview of all the available LL/SC +instruction pairs in the A64 instruction set. + +Following code gives a starting point to play with the LL/SC instructions on +ARM, by providing inline assembly wrapper functions to emit the LDXR and STXR +instructions. + +```cpp +/// Load exclusive register. +inline uint64_t ldxr(uint64_t* addr) { + uint64_t ret; + asm volatile("ldxr %0, %1" : "=r"(ret) : "Q"(*addr) : "memory"); + return ret; +} + +/// Store exclusive register. +inline bool stxr(uint64_t* addr, uint64_t val) { + uint32_t ret; + asm volatile("stxr %w0, %2, %1" : "=&r"(ret), "=Q"(*addr) : "r"(val) : "memory"); + return ret == 0; +} + +int main() { + uint64_t mem = 42; + + auto T1 = std::thread([&mem]() { + // Write to exclusive location (does clear exclusive monitor). + mem = 2222; + // Full memory barrier. + __sync_synchronize(); + }); + + uint64_t old = ldxr(&mem); + + // Some artificial delay w/o an explicit context switch (eg syscall) as that + // would clear the exclusive monitor, though it can still be interupted by + // the scheduler. + // Delay is "tuned" for my ARM device. + for (int i = 0; i < (1 << 14); ++i) { + asm volatile("nop"); + } + + // Full memory barrier. + __sync_synchronize(); + + bool ok = stxr(&mem, 1111); + printf("old: %lu -> mem: %lu | ok: %d\n", old, mem, ok); + + T1.join(); + return ok ? 0 : 1; +} +``` + +The full source code with additional comments is available here +[a64-basic-llsc.cc](a64-basic-llsc.cc). It can be compiled and run natively on +an ARM system as shown below. +We run the program in a loop until we hit the case where T1 wins the race and +does the memory store in between the LDXR/STXR pair on the main thread. +``` +> g++ -o a64-basic-llsc a64-basic-llsc.cc -lpthread +> while ./a64-basic-llsc; do : ; done +... +old: 42 -> mem: 1111 | ok: 1 +old: 42 -> mem: 1111 | ok: 1 +old: 42 -> mem: 1111 | ok: 1 +old: 42 -> mem: 2222 | ok: 0 +``` + +With those basics covered we can also implement the LL/SC version of the +`BlockStack`. The push() and pop() methods now leverage the exclusive load and +store instructions to atomically update the `m_head` pointer. + +```cpp +struct BlockStack { + struct Block { + Block* next{nullptr}; + // .. + }; + + void push(Block* blk) { + do { + blk->next = load_head_ex(); + } while (!store_head_ex(blk)); + } + + Block* pop() { + Block *blk, *next; + do { + blk = load_head_ex(); + next = blk->next; + } while (!store_head_ex(next)); + return blk; + } + +private: + // Assume 64bit ptr size for ldxr/stxr instructions. + static_assert(sizeof(void*) == sizeof(uint64_t), "Ptr size miss-match!"); + + Block* load_head_ex() const { + Block* blk = (Block*)ldxr((uint64_t*)&m_head); + mem_barrier(); + return blk; + } + + bool store_head_ex(Block* blk) { + const bool success = stxr((uint64_t*)&m_head, (uint64_t)blk); + mem_barrier(); + return success; + } + + Block* m_head{nullptr}; +}; +``` + +The full version of this implementation with additional comments as explanation +is available here [a64-llsc.cc](a64-llsc.cc). It can be compiled and run as +follows on some ARM system. Depending on the timing, the example must be run +multiple times to pass the assertions, as all the artificial delays hacked into +the implementation are tuned according to my ARM device. +``` +g++ -o a64-llsc a64-llsc.cc -lpthread +./a64-llsc +``` + +In fact, passing the assertions in the example above actually does not +guarantee that we drive the two threads and the BlockData object exactly into +the state as in the CAS example, which triggered the ABA problem. In the CAS +example we could use a _sleep()_ to control the exact order of events, but here +we can neither use any _sleep()s_ nor any _atomics_ to synchronize the threads +execution, as any of those would most certainly lead to clearing the exclusive +monitor of the _ldxr_ instruction to read the head pointer. +All we can do is to add some _nop_ delays, hoping the exclusive monitor is not +cleared due to any of those artificial hacks we introduced (or some +interruption by the scheduler), praise the order of events is exactly as we +want them to be and feel happy when the assertions pass. + +The sources of the example are good to explain how the stronger guarantees of +the LL/SC instructions prevent the ABA problem. But in practice, it is really +hard to prove that the program ran exactly as we wanted it to :^) + +## The END + +Personally, I would say that in 99% of the cases, nobody will have to write +code as we did in this post. Of course, there are always exceptions. + +Always prefer to write portable and generic algorithms, and start with a lock +rather than a lock-free algorithm. And most important, measure and profile your +code and do not do premature optimizations and use "cool" stuff just for the +sake of using it. Use the right tool for the job and seek for the simplest +solution. + +However, we specifically wanted to write code like this to introduce CAS vs +LL/SC atomics and the ABA problem in multi-threaded lock-free algorithms. + +Any corrections, improvements or comments are happily welcome, for that find my +mail on [memzero.de][memzero] + +## Appendix: QEMU ARM user emulation + +Unfortunately, we can not use the `qemu-aarch64` userspace emulator for the +experiments here, as for the ARM guest target, QEMU implements the load / store +exclusive instructions not with the full architectural semantics, but rather a +simplification which seems sufficient for the typical guest images. + +When QEMU emulates a _load exclusive_ instruction, it saves the guest address +as well as the data loaded from the guest memory in the cpu state +[CPUARMState::{exclusive_addr, exclusive_val}][qemu-a64-cpu-state]. On a _store +exclusive_, the guest address of the store is compared with the saved +_exclusive_addr_ and the data to be written with the saved _exclusive_val_. +Iff both compare true, the _store exclusive_ is carried out successfully. + +The following pseudo code taken from a comment in +[QEMU:ranslate-a64.c][qemu-a64-ex] shows how QEMU emits _store exclusive_ +instructions where `env` refers to the cpu state. +```c +/// gen_store_exclusive(..) +if (env->exclusive_addr == addr && env->exclusive_val == [addr]) { + [addr] = {Rt}; + {Rd} = 0; +} else { + {Rd} = 1; +} +env->exclusive_addr = -1; +``` + +The *exclusive_val* and *exclusive_addr* fields act as _exclusive monitor_. +However, they are only maintained on load / store exclusive instructions and +not in normal store instructions and this is where QEMU weakens the semantics +of the exclusive instructions. A sequence of normal write instructions can +invalidate and restore the state of the "_exclusive monitor_" in the qemu cpu +state. + +With everything learned about the ABA problem in this post, one can now +understand why QEMU is a bad idea for running our experiments. QEMU will just +trigger the ABA problem even with our [a64-llsc.cc](a64-llsc.cc) example using +load / store exclusive instructions. + +## Abbreviations +- CAS: compare-and-swap +- LL/SC: load-linked store-conditional +- CISC: complex instruction set computer +- RISC: reduced instruction set computer + +## Source files +- [cas-aba-issue.cc](cas-aba-issue.cc) +- [cas-ptr-generation.cc](cas-ptr-generation.cc) +- [a64-basic-llsc.cc](a64-basic-llsc.cc) +- [a64-llsc.cc](a64-llsc.cc) + +## References +- [std::atomic][cpp-std-atomic] +- [cpp memory ordering][cpp-mem-order] +- [Fedor Pikus "C++ atomics, from basic to advanced. What do they really do?"][yt-atomics] +- [Memory Consistency Models: A Tutorial][mem-order-tutorial] +- [gcc _sync_ builtins][gcc-sync] +- [gcc inline asm: output constraints][gcc-asm-output] +- [gcc inline asm: machine constraints][gcc-asm-machine] +- [x86_64: cmpxchg16b instruction][cmpxchg16b] +- [ARM architecture reference manual A-profile][arm-arm-a] +- [ARM exclusive monitor][a64-ex-monitor] +- [ARM A64: instruction manual][a64-insn] +- [ARM A64: stxr instruction][a64-stxr] +- [ARM A64: ldxr instruction][a64-ldxr] +- [arm64: atomics: fix use of acquire + release for full barrier semantics][linux-a64-patch] +- [QEMU A64 load / store exclusive][qemu-a64-ex] +- [QEMU A64 CPUARMState::{exclusive_addr, exclusive_val}][qemu-a64-cpu-state] + +[fly-you-fools]: https://media.giphy.com/media/v1.Y2lkPTc5MGI3NjExMWJpdjFmMGlmdjcyaWZqM21yaXlzb3JlamN6NGJjZmwzdHkxOWlieCZlcD12MV9pbnRlcm5hbF9naWZfYnlfaWQmY3Q9Zw/74ExXfqsbJ8l2/giphy.gif +[yt-atomics]: https://youtu.be/ZQFzMfHIxng?t=2596 +[cpp-mem-order]: https://en.cppreference.com/w/cpp/atomic/memory_order +[cpp-std-atomic]: https://en.cppreference.com/w/cpp/atomic/atomic +[gcc-sync]: https://gcc.gnu.org/onlinedocs/gcc/_005f_005fsync-Builtins.html +[gcc-asm-machine]: https://gcc.gnu.org/onlinedocs/gcc/Machine-Constraints.html +[gcc-asm-output]: https://gcc.gnu.org/onlinedocs/gcc/Extended-Asm.html#Output-Operands +[cmpxchg16b]: https://www.felixcloutier.com/x86/cmpxchg8b:cmpxchg16b +[a64-insn]: https://developer.arm.com/documentation/ddi0602/latest/?lang=en +[a64-stxr]: https://developer.arm.com/documentation/ddi0596/latest/Base-Instructions/STXR--Store-Exclusive-Register-?lang=en +[a64-ldxr]: https://developer.arm.com/documentation/ddi0596/latest/Base-Instructions/LDXR--Load-Exclusive-Register-?lang=en +[a64-ex-monitor]: https://developer.arm.com/documentation/100934/0100/Exclusive-monitors?lang=en +[arm-arm-a]: https://developer.arm.com/documentation/ddi0487/latest/ +[memzero]: https://memzero.de +[qemu-a64-ex]: https://elixir.bootlin.com/qemu/v8.1.2/source/target/arm/tcg/translate-a64.c#L2411 +[qemu-a64-cpu-state]: https://elixir.bootlin.com/qemu/v8.1.2/source/target/arm/cpu.h#L687 +[mem-order-tutorial]: https://www.cs.utexas.edu/~bornholt/post/memory-models.html +[linux-a64-patch]: https://patchwork.kernel.org/project/linux-arm-kernel/patch/1391516953-14541-1-git-send-email-will.deacon@arm.com/ diff --git a/content/2023-09-01-cas-llsc-aba/list-abc.drawio b/content/2023-09-01-cas-llsc-aba/list-abc.drawio new file mode 100644 index 0000000..a099e1a --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/list-abc.drawio @@ -0,0 +1,61 @@ +<mxfile host="Electron" modified="2023-09-04T21:48:26.222Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.8 Chrome/114.0.5735.289 Electron/25.5.0 Safari/537.36" etag="5jbSRgdClgZPMRSnSHY9" version="21.6.8" type="device"> + <diagram name="Page-1" id="oOsWoI9GtXahTL3aIcgJ"> + <mxGraphModel dx="1434" dy="907" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0"> + <root> + <mxCell id="0" /> + <mxCell id="1" parent="0" /> + <mxCell id="otOk63rFS2EbfHY-E8zC-1" value="A" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=monospace;fillColor=none;verticalAlign=middle;strokeColor=#55FFFF;" parent="1" vertex="1"> + <mxGeometry x="80" y="80" width="60" height="40" as="geometry" /> + </mxCell> + <mxCell id="R_8vHYw_WuqzZSNNfNo4-1" value="next" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;horizontal=0;fontFamily=monospace;strokeColor=#55FFFF;" parent="1" vertex="1"> + <mxGeometry x="140" y="80" width="20" height="40" as="geometry" /> + </mxCell> + <mxCell id="R_8vHYw_WuqzZSNNfNo4-3" value="B" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=monospace;fillColor=none;verticalAlign=middle;strokeColor=#FF55FF;" parent="1" vertex="1"> + <mxGeometry x="220" y="80" width="60" height="40" as="geometry" /> + </mxCell> + <mxCell id="R_8vHYw_WuqzZSNNfNo4-4" value="<span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span>" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;horizontal=0;fontFamily=monospace;strokeColor=#FF55FF;" parent="1" vertex="1"> + <mxGeometry x="280" y="80" width="20" height="40" as="geometry" /> + </mxCell> + <mxCell id="R_8vHYw_WuqzZSNNfNo4-5" value="C" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=monospace;fillColor=none;verticalAlign=middle;strokeColor=#66FF66;" parent="1" vertex="1"> + <mxGeometry x="360" y="80" width="60" height="40" as="geometry" /> + </mxCell> + <mxCell id="R_8vHYw_WuqzZSNNfNo4-6" value="<span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span>" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;horizontal=0;fontFamily=monospace;strokeColor=#66FF66;" parent="1" vertex="1"> + <mxGeometry x="420" y="80" width="20" height="40" as="geometry" /> + </mxCell> + <mxCell id="R_8vHYw_WuqzZSNNfNo4-7" value="" style="endArrow=block;html=1;rounded=0;entryX=0.022;entryY=0.476;entryDx=0;entryDy=0;endFill=1;startArrow=none;startFill=0;entryPerimeter=0;fontFamily=monospace;" parent="1" source="R_8vHYw_WuqzZSNNfNo4-1" target="R_8vHYw_WuqzZSNNfNo4-3" edge="1"> + <mxGeometry width="50" height="50" relative="1" as="geometry"> + <mxPoint x="160" y="100" as="sourcePoint" /> + <mxPoint x="300" y="170" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="R_8vHYw_WuqzZSNNfNo4-8" value="" style="endArrow=block;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;endFill=1;startArrow=none;startFill=0;fontFamily=monospace;" parent="1" source="R_8vHYw_WuqzZSNNfNo4-4" target="R_8vHYw_WuqzZSNNfNo4-5" edge="1"> + <mxGeometry width="50" height="50" relative="1" as="geometry"> + <mxPoint x="320" y="150" as="sourcePoint" /> + <mxPoint x="400" y="130.5" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="R_8vHYw_WuqzZSNNfNo4-12" value="m_head" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;fontFamily=monospace;" parent="1" vertex="1"> + <mxGeometry x="10" y="40" width="60" height="20" as="geometry" /> + </mxCell> + <mxCell id="R_8vHYw_WuqzZSNNfNo4-13" value="" style="endArrow=block;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endFill=1;startArrow=none;startFill=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;fontFamily=monospace;edgeStyle=orthogonalEdgeStyle;" parent="1" source="R_8vHYw_WuqzZSNNfNo4-12" target="otOk63rFS2EbfHY-E8zC-1" edge="1"> + <mxGeometry width="50" height="50" relative="1" as="geometry"> + <mxPoint x="30" y="40" as="sourcePoint" /> + <mxPoint x="91" y="39" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="vAtrAxF_xAnrCS7FAYcJ-1" value="" style="endArrow=block;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;endFill=1;startArrow=none;startFill=0;fontFamily=monospace;" parent="1" edge="1"> + <mxGeometry width="50" height="50" relative="1" as="geometry"> + <mxPoint x="440" y="99.71000000000001" as="sourcePoint" /> + <mxPoint x="500" y="99.71000000000001" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="vAtrAxF_xAnrCS7FAYcJ-2" value=".." style="rounded=0;whiteSpace=wrap;html=1;fontFamily=monospace;fillColor=none;verticalAlign=middle;strokeColor=#F0F0F0;" parent="1" vertex="1"> + <mxGeometry x="500" y="80" width="60" height="40" as="geometry" /> + </mxCell> + <mxCell id="vAtrAxF_xAnrCS7FAYcJ-3" value="<span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span>" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;horizontal=0;fontFamily=monospace;strokeColor=#F0F0F0;" parent="1" vertex="1"> + <mxGeometry x="560" y="80" width="20" height="40" as="geometry" /> + </mxCell> + </root> + </mxGraphModel> + </diagram> +</mxfile> diff --git a/content/2023-09-01-cas-llsc-aba/list-abc.svg b/content/2023-09-01-cas-llsc-aba/list-abc.svg new file mode 100644 index 0000000..a80e38b --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/list-abc.svg @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Do not edit this file with editors other than draw.io --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="571px" height="81px" viewBox="-0.5 -0.5 571 81" content="<mxfile host="Electron" modified="2023-09-04T21:48:37.785Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.8 Chrome/114.0.5735.289 Electron/25.5.0 Safari/537.36" etag="YY9FpRNf6eKDXMsoXZFg" version="21.6.8" type="device"><diagram name="Page-1" id="oOsWoI9GtXahTL3aIcgJ">7Vpbb+o4EP4t+4C0+0CVG6E8AiWnWmm7R8vDOexLZRKTWDh2jjG3/vq1Y+ceSmmB5QEqlXhmPI7n+zyTjOjY43j3jYEk+osGEHcsI9h17KeOZTl9S/yXgr0SPLqGEoQMBUpkFoIpeoNamJmtUQBXFUNOKeYoqQp9Sgj0eUUGGKPbqtmC4uqqCQhhQzD1AW5Kf6CAR3oXPaOQP0MURtnKpqE1MciMtWAVgYBuSyJ70rHHjFKuruLdGGIZuywuap53QJvfGIOEf2QC5X8vXZt5U2syXzzPupPHt3FXe9kAvNYbHuq75fssBIyuSQClF6Njj7YR4nCaAF9qtwJzIYt4jMXIFJcLSrgHYoQl3jEldKVMRwuE8ZhiyoScUCJFG8g4EqEeYhQSaY6CQC46WnFGlzCz7lh2r+eJj9A0N53tQPiCu5JIB+EbpDHkbC9MtPZR47GvDrcFuhlFoxKwjpYBzacw91uEXFzoqLcj8M/r4+Z5tn39sf719u/05WXxQp0WBAjc8S+C0Ix1RBl6E9gArF0cBOqiwTedo9G3rhl9uxH90S3y3/MkCOeBIA/wjRwApwFBx3Ixl2FIAKlA4f5ay2w58lVkRKoyWDj/Pb03sbhRvvgjjZYh8eiu0rIizU0r2ZUVyrXUEMpieThy3QYwBMS3AAfwNZNF6F07HySHTLY6jlLpGIbSYMg5ZF3JD0TC5kzKkggQ7dJSMgEx7wJFFyn2BQ0gK+mQYCrRKxnZVlMNZ8LZQvjPVkopqHBPq2RpmS1lQfXGcl9iL/MlEu6kT8XTrmZOxW4O/GWYnpxuHa2+xmigvu0MqtRlAH3KAEeUdHmE/CWBK31viCCOsuDUbUtAvmtXupeK3QJTwOuRCdAqwWCfmWMkFJbxG4oTyjiQYR4VnBRXofxW2VsRWJwHxWGluqWkftaMcrykXjWp9xoZZXyLSd11Pc91zwOB7d5WUnfvSf2e1O9J/cpJ/ZwZxTn+mHjVpN5vySi1yEMSDOUrvxjNMfWX1VBXcRGBYfufcvBgyDAowSwVOH03EzztyvZP+3wUeAhnnlccMJ6trFFMZdomn/8dMiSiIQ/3+8gexG9F18yHx18nxeoh5MdffGBQaX802VCCu9cCdyZjEIuzuKk2Tdo4oFf4TpHYWfFaWCtfeRclc6H2rWeVmxw1R7ZRc9SvOVKBaThKKZlv+/MsfbwUS6sM7R3j5w7xn9qjvC7NEqNikhx8ntMXoK/zQfr2boq+di1Xmr1P0tep09eWqF2TwKbVYHD8GkEQnL3Onc6eE9pM1TA6H3sati5Vu8xmm+n/SQunly2dSIrkMSullfZEchBYeVqnesfiYSuiISUATwrpV+qe1cwc73W8byVztBP11LwxMCtu7MGlcsZmyNlw573uhoSNp31vOPP/bGlh34teiQ03QjWn1nofDB764jkr/5ifo17POMnthanYrF4PD1+sXJdp6Bvy7zzFro7AFXs/rRi0Fbt77+fe+7n3fi7Z+zlrRjneTT5T70cMi19AqCJQ/IzEnvwH</diagram></mxfile>"><defs/><g><rect x="70" y="40" width="60" height="40" fill="none" stroke="#55ffff" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 60px; margin-left: 71px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">A</div></div></div></foreignObject><text x="100" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">A</text></switch></g><rect x="130" y="40" width="20" height="40" fill="none" stroke="#55ffff" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 140 60)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 60px; margin-left: 121px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">next</div></div></div></foreignObject><text x="140" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">next</text></switch></g><rect x="210" y="40" width="60" height="40" fill="none" stroke="#ff55ff" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 60px; margin-left: 211px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">B</div></div></div></foreignObject><text x="240" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">B</text></switch></g><rect x="270" y="40" width="20" height="40" fill="none" stroke="#ff55ff" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 280 60)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 60px; margin-left: 261px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span></div></div></div></foreignObject><text x="280" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">next</text></switch></g><rect x="350" y="40" width="60" height="40" fill="none" stroke="#66ff66" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 60px; margin-left: 351px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">C</div></div></div></foreignObject><text x="380" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">C</text></switch></g><rect x="410" y="40" width="20" height="40" fill="none" stroke="#66ff66" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 420 60)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 60px; margin-left: 401px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span></div></div></div></foreignObject><text x="420" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">next</text></switch></g><path d="M 150 59.87 L 203.2 59.15" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 210.2 59.06 L 203.25 62.65 L 203.16 55.65 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><path d="M 290 60 L 341.88 60" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 348.88 60 L 341.88 63.5 L 341.88 56.5 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><rect x="0" y="0" width="60" height="20" fill="none" stroke="rgb(240, 240, 240)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 10px; margin-left: 1px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">m_head</div></div></div></foreignObject><text x="30" y="14" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">m_head</text></switch></g><path d="M 30 20 L 30 60 L 61.88 60" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 68.88 60 L 61.88 63.5 L 61.88 56.5 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><path d="M 430 59.71 L 481.88 59.71" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 488.88 59.71 L 481.88 63.21 L 481.88 56.21 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><rect x="490" y="40" width="60" height="40" fill="none" stroke="#f0f0f0" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 60px; margin-left: 491px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">..</div></div></div></foreignObject><text x="520" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">..</text></switch></g><rect x="550" y="40" width="20" height="40" fill="none" stroke="#f0f0f0" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 560 60)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 60px; margin-left: 541px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span></div></div></div></foreignObject><text x="560" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">next</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg>
\ No newline at end of file diff --git a/content/2023-09-01-cas-llsc-aba/list-ac.drawio b/content/2023-09-01-cas-llsc-aba/list-ac.drawio new file mode 100644 index 0000000..859b9e1 --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/list-ac.drawio @@ -0,0 +1,58 @@ +<mxfile host="Electron" modified="2023-09-04T21:49:08.573Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.8 Chrome/114.0.5735.289 Electron/25.5.0 Safari/537.36" etag="NL8l6Snm0eBt3uoWnQaY" version="21.6.8" type="device"> + <diagram name="Page-1" id="uBcNzIc841T6t-VShr4X"> + <mxGraphModel dx="472" dy="860" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0"> + <root> + <mxCell id="0" /> + <mxCell id="1" parent="0" /> + <mxCell id="PaWd9qBO0cvwb-g6CXnF-1" value="A" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=monospace;fillColor=none;verticalAlign=middle;strokeColor=#55FFFF;" parent="1" vertex="1"> + <mxGeometry x="80" y="80" width="60" height="40" as="geometry" /> + </mxCell> + <mxCell id="PaWd9qBO0cvwb-g6CXnF-2" value="next" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;horizontal=0;fontFamily=monospace;strokeColor=#55FFFF;" parent="1" vertex="1"> + <mxGeometry x="140" y="80" width="20" height="40" as="geometry" /> + </mxCell> + <mxCell id="PaWd9qBO0cvwb-g6CXnF-5" value="C" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=monospace;fillColor=none;verticalAlign=middle;strokeColor=#66FF66;" parent="1" vertex="1"> + <mxGeometry x="360" y="80" width="60" height="40" as="geometry" /> + </mxCell> + <mxCell id="PaWd9qBO0cvwb-g6CXnF-6" value="<span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span>" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;horizontal=0;fontFamily=monospace;strokeColor=#66FF66;" parent="1" vertex="1"> + <mxGeometry x="420" y="80" width="20" height="40" as="geometry" /> + </mxCell> + <mxCell id="PaWd9qBO0cvwb-g6CXnF-7" value="" style="endArrow=block;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endFill=1;startArrow=none;startFill=0;fontFamily=monospace;" parent="1" source="PaWd9qBO0cvwb-g6CXnF-2" target="PaWd9qBO0cvwb-g6CXnF-5" edge="1"> + <mxGeometry width="50" height="50" relative="1" as="geometry"> + <mxPoint x="160" y="100" as="sourcePoint" /> + <mxPoint x="221.32000000000005" y="99.03999999999996" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="PaWd9qBO0cvwb-g6CXnF-9" value="m_head" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;fontFamily=monospace;" parent="1" vertex="1"> + <mxGeometry x="10" y="40" width="60" height="20" as="geometry" /> + </mxCell> + <mxCell id="PaWd9qBO0cvwb-g6CXnF-10" value="" style="endArrow=block;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endFill=1;startArrow=none;startFill=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;fontFamily=monospace;edgeStyle=orthogonalEdgeStyle;" parent="1" source="PaWd9qBO0cvwb-g6CXnF-9" target="PaWd9qBO0cvwb-g6CXnF-1" edge="1"> + <mxGeometry width="50" height="50" relative="1" as="geometry"> + <mxPoint x="30" y="40" as="sourcePoint" /> + <mxPoint x="91" y="39" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="PaWd9qBO0cvwb-g6CXnF-11" value="" style="endArrow=block;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;endFill=1;startArrow=none;startFill=0;fontFamily=monospace;" parent="1" edge="1"> + <mxGeometry width="50" height="50" relative="1" as="geometry"> + <mxPoint x="440" y="99.71000000000001" as="sourcePoint" /> + <mxPoint x="500" y="99.71000000000001" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="PaWd9qBO0cvwb-g6CXnF-12" value=".." style="rounded=0;whiteSpace=wrap;html=1;fontFamily=monospace;fillColor=none;verticalAlign=middle;strokeColor=#F0F0F0;" parent="1" vertex="1"> + <mxGeometry x="500" y="80" width="60" height="40" as="geometry" /> + </mxCell> + <mxCell id="PaWd9qBO0cvwb-g6CXnF-13" value="<span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span>" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;horizontal=0;fontFamily=monospace;strokeColor=#F0F0F0;" parent="1" vertex="1"> + <mxGeometry x="560" y="80" width="20" height="40" as="geometry" /> + </mxCell> + <mxCell id="PaWd9qBO0cvwb-g6CXnF-14" value="B" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=monospace;fillColor=none;verticalAlign=middle;strokeColor=#FF55FF;" parent="1" vertex="1"> + <mxGeometry x="80" y="160" width="60" height="40" as="geometry" /> + </mxCell> + <mxCell id="PaWd9qBO0cvwb-g6CXnF-15" value="<span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span>" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;horizontal=0;fontFamily=monospace;strokeColor=#FF55FF;" parent="1" vertex="1"> + <mxGeometry x="140" y="160" width="20" height="40" as="geometry" /> + </mxCell> + <mxCell id="PaWd9qBO0cvwb-g6CXnF-19" value="Thread 2" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;fontFamily=monospace;align=left;" parent="1" vertex="1"> + <mxGeometry x="80" y="140" width="80" height="20" as="geometry" /> + </mxCell> + </root> + </mxGraphModel> + </diagram> +</mxfile> diff --git a/content/2023-09-01-cas-llsc-aba/list-ac.svg b/content/2023-09-01-cas-llsc-aba/list-ac.svg new file mode 100644 index 0000000..9371124 --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/list-ac.svg @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Do not edit this file with editors other than draw.io --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="571px" height="161px" viewBox="-0.5 -0.5 571 161" content="<mxfile host="Electron" modified="2023-09-04T21:49:18.993Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.8 Chrome/114.0.5735.289 Electron/25.5.0 Safari/537.36" etag="y2ehA6rUJ7szo54y6HDr" version="21.6.8" type="device"><diagram name="Page-1" id="uBcNzIc841T6t-VShr4X">7Vpbj9o6EP4t5wHpnAdQLpBdHhd2aV+qVtpK3T4dmcQkFo6dGnPrr+84tskNlmW5LJVgpRLPTGbs7xuPL7TlD9PVJ4Gy5AuPMG15TrRq+Y8tz+veefCvEqy14D5wtCAWJNIitxA8k9/YCK3ZnER4VjGUnFNJsqow5IzhUFZkSAi+rJpNOK1GzVCMG4LnENGm9AeJZGJG0XMK+WdM4sRGdh2jSZE1NoJZgiK+LIn8p5Y/FJxL/ZSuhpgq7Cwu+r3RDu2mYwIz+ZYXvqEfUf/X4KsTLpbjdhwMX9iobbwsEJ2bAT+Y3sq1hUDwOYuw8uK0/MEyIRI/ZyhU2iVwDrJEphRaLjxOOJMjlBKq+E454zNtOpgQSoeccgFyxpkSLbCQBKB+oCRmypxEkQo6mEnBp9hatzy/1xvBBzTNQdsRgC+8KokMCJ8wT7EUazAx2nvDx7raXBbs2hRNSsR2jQyZfIo3fgvI4cGgfgADXoMBhlfySBKaWCdckN/ADaLGxU6izgq+292LvndJ9HsN9IfXmP9BMBoFwWko8IPrmgBBg4KWF1CpYMgQq1AR/JqrajkINTJQqhwRj//N+wbBnfLDfzlajuKjPcuXFWXuetmqrNCulYZxkarJsdEtkCAIvoEcJOdCLUKv2oUo22WyNDgqZddxtIZiKbFoq/wgLG6+yUWWIGZceloGFMs20umixCGkARYlHYFMZSaSY4eaa6QAZxPwbyPlKah5z1fJUpglF1G1YxtfMJbxlIA75VPnadtkTsVujMJpnM+cdp2tO8NRX3/7lqrcZYRDLpAknLVlQsIpwzPTN8KIJBacum2JyFftSn2p2E0oR7KOTERmGUVra04JKDznH5JmXEikYB4UOQlPsfrW1VsnMMwHncNadU1F/ZQVpetdV1G/21JRashjFj2o3SG0xpSH0yrUVV4AGLF+KTd+qkanZ5uPq7Lycb1pRSNCrc+ZRELamIa/XGZsXqVvJ0kzPhch3r+9gEAxlvsXQhxVtsNNykuc9rZwamUCU5hwi+omehvRJsI3TmBkxTahtkZtdtXWhR63eau86a05glc7PpSlzadX8dvvdxy/X3yCahSNWiNKnpQbTN6fp/1Gnqb/JxhFJ68Th+fVATu6KlPdt+0mvHPNfdudv2/y4xWRL4VvaP00b6vnIoxq2Cg7iVXz+NmMGBarhMecIfpUSI8oKf03lhT3qkqKvz1PDy0ofbfixu9ftGK4zfP6x2S3yVW3lKlF3m7P1Ussh1eSa93aKReWmTu3tAi578u9nnOQ23PnYvPmotM5cuk6y+F55Ki/06x2dQo++vDs+lsKwu30fDs9307P5zw9n7Sk7L+Pu+jp2e02Ssqx2J+nrI/UxfRpOKj9JrA5fn5YWW/eS9/K+q2s38r6mcv6CUtK/ZeubTXlsnW9ed30PRH5dZPTPEUey0IV2333UGb2PlI8kaeBv17Rt9xL3W9B/x33UtAsfsTXh6vif0L4T38A</diagram></mxfile>"><defs/><g><rect x="70" y="40" width="60" height="40" fill="none" stroke="#55ffff" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 60px; margin-left: 71px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">A</div></div></div></foreignObject><text x="100" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">A</text></switch></g><rect x="130" y="40" width="20" height="40" fill="none" stroke="#55ffff" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 140 60)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 60px; margin-left: 121px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">next</div></div></div></foreignObject><text x="140" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">next</text></switch></g><rect x="350" y="40" width="60" height="40" fill="none" stroke="#66ff66" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 60px; margin-left: 351px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">C</div></div></div></foreignObject><text x="380" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">C</text></switch></g><rect x="410" y="40" width="20" height="40" fill="none" stroke="#66ff66" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 420 60)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 60px; margin-left: 401px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span></div></div></div></foreignObject><text x="420" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">next</text></switch></g><path d="M 150 60 L 341.88 60" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 348.88 60 L 341.88 63.5 L 341.88 56.5 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><rect x="0" y="0" width="60" height="20" fill="none" stroke="rgb(240, 240, 240)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 10px; margin-left: 1px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">m_head</div></div></div></foreignObject><text x="30" y="14" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">m_head</text></switch></g><path d="M 30 20 L 30 60 L 61.88 60" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 68.88 60 L 61.88 63.5 L 61.88 56.5 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><path d="M 430 59.71 L 481.88 59.71" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 488.88 59.71 L 481.88 63.21 L 481.88 56.21 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><rect x="490" y="40" width="60" height="40" fill="none" stroke="#f0f0f0" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 60px; margin-left: 491px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">..</div></div></div></foreignObject><text x="520" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">..</text></switch></g><rect x="550" y="40" width="20" height="40" fill="none" stroke="#f0f0f0" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 560 60)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 60px; margin-left: 541px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span></div></div></div></foreignObject><text x="560" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">next</text></switch></g><rect x="70" y="120" width="60" height="40" fill="none" stroke="#ff55ff" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 140px; margin-left: 71px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">B</div></div></div></foreignObject><text x="100" y="144" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">B</text></switch></g><rect x="130" y="120" width="20" height="40" fill="none" stroke="#ff55ff" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 140 140)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 140px; margin-left: 121px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span></div></div></div></foreignObject><text x="140" y="144" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">next</text></switch></g><rect x="70" y="100" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 78px; height: 1px; padding-top: 110px; margin-left: 72px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">Thread 2</div></div></div></foreignObject><text x="72" y="114" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px">Thread 2</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg>
\ No newline at end of file diff --git a/content/2023-09-01-cas-llsc-aba/list-b.drawio b/content/2023-09-01-cas-llsc-aba/list-b.drawio new file mode 100644 index 0000000..13a14e2 --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/list-b.drawio @@ -0,0 +1,55 @@ +<mxfile host="Electron" modified="2023-09-04T21:49:45.314Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.8 Chrome/114.0.5735.289 Electron/25.5.0 Safari/537.36" etag="1jnZ21qDnCbH1Ves6_se" version="21.6.8" type="device"> + <diagram name="Page-1" id="fIKG6hPY-mFjmqtQkcuF"> + <mxGraphModel dx="472" dy="860" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="850" pageHeight="1100" math="0" shadow="0"> + <root> + <mxCell id="0" /> + <mxCell id="1" parent="0" /> + <mxCell id="3W4Jywxm7-PNISn2kuxM-3" value="C" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=monospace;fillColor=none;verticalAlign=middle;strokeColor=#66FF66;" parent="1" vertex="1"> + <mxGeometry x="360" y="80" width="60" height="40" as="geometry" /> + </mxCell> + <mxCell id="3W4Jywxm7-PNISn2kuxM-4" value="<span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span>" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;horizontal=0;fontFamily=monospace;strokeColor=#66FF66;" parent="1" vertex="1"> + <mxGeometry x="420" y="80" width="20" height="40" as="geometry" /> + </mxCell> + <mxCell id="3W4Jywxm7-PNISn2kuxM-6" value="m_head" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;fontFamily=monospace;" parent="1" vertex="1"> + <mxGeometry x="10" y="40" width="60" height="20" as="geometry" /> + </mxCell> + <mxCell id="3W4Jywxm7-PNISn2kuxM-7" value="" style="endArrow=block;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;endFill=1;startArrow=none;startFill=0;exitX=0.5;exitY=1;exitDx=0;exitDy=0;fontFamily=monospace;edgeStyle=orthogonalEdgeStyle;" parent="1" source="3W4Jywxm7-PNISn2kuxM-6" target="3W4Jywxm7-PNISn2kuxM-11" edge="1"> + <mxGeometry width="50" height="50" relative="1" as="geometry"> + <mxPoint x="30" y="40" as="sourcePoint" /> + <mxPoint x="80" y="100" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="3W4Jywxm7-PNISn2kuxM-8" value="" style="endArrow=block;html=1;rounded=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;exitX=1;exitY=0.5;exitDx=0;exitDy=0;endFill=1;startArrow=none;startFill=0;fontFamily=monospace;" parent="1" edge="1"> + <mxGeometry width="50" height="50" relative="1" as="geometry"> + <mxPoint x="440" y="99.71000000000001" as="sourcePoint" /> + <mxPoint x="500" y="99.71000000000001" as="targetPoint" /> + </mxGeometry> + </mxCell> + <mxCell id="3W4Jywxm7-PNISn2kuxM-9" value=".." style="rounded=0;whiteSpace=wrap;html=1;fontFamily=monospace;fillColor=none;verticalAlign=middle;strokeColor=#F0F0F0;" parent="1" vertex="1"> + <mxGeometry x="500" y="80" width="60" height="40" as="geometry" /> + </mxCell> + <mxCell id="3W4Jywxm7-PNISn2kuxM-10" value="<span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span>" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;horizontal=0;fontFamily=monospace;strokeColor=#F0F0F0;" parent="1" vertex="1"> + <mxGeometry x="560" y="80" width="20" height="40" as="geometry" /> + </mxCell> + <mxCell id="3W4Jywxm7-PNISn2kuxM-11" value="B" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=monospace;fillColor=none;verticalAlign=middle;strokeColor=#FF55FF;" parent="1" vertex="1"> + <mxGeometry x="80" y="160" width="60" height="40" as="geometry" /> + </mxCell> + <mxCell id="3W4Jywxm7-PNISn2kuxM-12" value="<span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span>" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;horizontal=0;fontFamily=monospace;strokeColor=#FF55FF;" parent="1" vertex="1"> + <mxGeometry x="140" y="160" width="20" height="40" as="geometry" /> + </mxCell> + <mxCell id="3W4Jywxm7-PNISn2kuxM-13" value="Thread 2" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;fontFamily=monospace;align=left;" parent="1" vertex="1"> + <mxGeometry x="80" y="140" width="80" height="20" as="geometry" /> + </mxCell> + <mxCell id="3W4Jywxm7-PNISn2kuxM-14" value="A" style="rounded=0;whiteSpace=wrap;html=1;fontFamily=monospace;fillColor=none;verticalAlign=middle;strokeColor=#55FFFF;" parent="1" vertex="1"> + <mxGeometry x="220" y="160" width="60" height="40" as="geometry" /> + </mxCell> + <mxCell id="3W4Jywxm7-PNISn2kuxM-15" value="next" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;horizontal=0;fontFamily=monospace;strokeColor=#55FFFF;" parent="1" vertex="1"> + <mxGeometry x="280" y="160" width="20" height="40" as="geometry" /> + </mxCell> + <mxCell id="3W4Jywxm7-PNISn2kuxM-16" value="Thread 1" style="rounded=0;whiteSpace=wrap;html=1;fillColor=none;strokeColor=none;fontFamily=monospace;align=left;" parent="1" vertex="1"> + <mxGeometry x="220" y="140" width="80" height="20" as="geometry" /> + </mxCell> + </root> + </mxGraphModel> + </diagram> +</mxfile> diff --git a/content/2023-09-01-cas-llsc-aba/list-b.svg b/content/2023-09-01-cas-llsc-aba/list-b.svg new file mode 100644 index 0000000..2bd0020 --- /dev/null +++ b/content/2023-09-01-cas-llsc-aba/list-b.svg @@ -0,0 +1,4 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- Do not edit this file with editors other than draw.io --> +<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"> +<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="571px" height="161px" viewBox="-0.5 -0.5 571 161" content="<mxfile host="Electron" modified="2023-09-04T21:49:54.834Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.8 Chrome/114.0.5735.289 Electron/25.5.0 Safari/537.36" etag="gpk-NX7OmYOtj85-XCb-" version="21.6.8" type="device"><diagram name="Page-1" id="fIKG6hPY-mFjmqtQkcuF">7VlNb9s4EP0tezCwPTjQl+XkGDtRuwt0USAF2p4KRqIlwhSppejY7q/vUCQtybLjpJEdH9wAtTgzGpJvHp9IaeBP89VHgYrsM08wHXhOshr4dwPPC8Ye/K8Ma224Dh1tSAVJtMmtDQ/kFzZGG7YgCS5bgZJzKknRNsacMRzLlg0JwZftsBmn7V4LlOKO4SFGtGv9RhKZmVmMnNr+CZM0sz27jvHkyAYbQ5mhhC8bJv9+4E8F51Jf5asppgo7i4u+L9rj3QxMYCZfcoP/Lfh3vVzl4+GX//55YN58sfo89HWWJ0QXZsJTM1q5thAIvmAJVlmcgT9ZZkTihwLFyruEmoMtkzmFlguXM85khHJCVb1zznipQyczQumUUy7AzjhTpicsJAGobylJmQonSaI6nZRS8Dm20QPPD8MoCkPwdCdtcFC58KphMiB8xDzHUqwhxHh9y0DLSNNc1uW1EVmjsoGxIUOodJO4xhwuDOyvKEHQKcHAC6lUMBSItUoR/r9QbJnEGplbcIr08e9qbNC507z4UKHlqHoMy2pZqXDXK1ZNh06tPIyLHNGG7wkJguAXioPkQqhF+GxcjIp9IUuDo3IGjqM9FEuJxVDxg7C0eycXRYaYSelpG5RYDpGmizLHQAMsGj4CTGWmJ8dOtfJIAclmkN/2VFFQ171SiUY3Sy6S9sA2uWAuj3MC6VROzdOhYU4r7hHF87RaOcPtao1NjW70r29LVaVMcMwFkoSzocxIPGe4NGMjjEhiwdmObRTy2bjGWFpxM8qR3EYmIWVB0dqGUwIOz/mL5AUXEimYJzUn4SpVvwx6tASG9aA5rF1vVJaugGRckF9AMDWLKsVe9TmqogTeQUXxTqkoYUdR8p8ZRknv+O+FuwdQ3TamwctU2jsWpuMdKr2FJmbJrdpxQOuR8njehq+NNeAi1t+bjR+qcTWyzbtV03m33rSSiFCbs5RISNunqUllMzHVHSsiv9e5ofXD3K2u625Uw/ayt644gc2RmTFoQMZTzhC9r637K1/yhYjxYdbC8FMsn4lzTUI1lmeJ1KDKaAdVrE1gCtr41N7v7eKP6eELJzC1ejuxm6c2gZ62uae5O9tKc91Os9lG2jwalk6eisubOf85va/PhN6GrG6DqjVxd5P19Uvi9bJ1JlwLgjZLbm6uxsCUzT/3z7g3cl6V9shUvOlQ8erqHM8kkaP++nnYbVfgvc8k9uF7OZRcDiWXQ8nJDiW9Ssrh1xwnPZTYbVtDUt6K/XFkPRqNoqifGmxv6sL3lnXvIusXWb/I+qllvUdJcYPDmnJaXe9+Qviaieptk9M9RL61Cm1sD72GMqv3juKZ7Af+bUXf8Vrqegf6R3st5Xa/Htye41NVLYC+loDnndtjddQpglam85GgXvF/wbbmtBLUfeG9kSC39yq8twR16H88DYJm/X1av9+pP/L7978B</diagram></mxfile>"><defs/><g><rect x="350" y="40" width="60" height="40" fill="none" stroke="#66ff66" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 60px; margin-left: 351px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">C</div></div></div></foreignObject><text x="380" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">C</text></switch></g><rect x="410" y="40" width="20" height="40" fill="none" stroke="#66ff66" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 420 60)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 60px; margin-left: 401px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span></div></div></div></foreignObject><text x="420" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">next</text></switch></g><rect x="0" y="0" width="60" height="20" fill="none" stroke="rgb(240, 240, 240)" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 10px; margin-left: 1px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">m_head</div></div></div></foreignObject><text x="30" y="14" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">m_head</text></switch></g><path d="M 30 20 L 30 140 L 61.88 140" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 68.88 140 L 61.88 143.5 L 61.88 136.5 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><path d="M 430 59.71 L 481.88 59.71" fill="none" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="stroke"/><path d="M 488.88 59.71 L 481.88 63.21 L 481.88 56.21 Z" fill="rgb(240, 240, 240)" stroke="rgb(240, 240, 240)" stroke-miterlimit="10" pointer-events="all"/><rect x="490" y="40" width="60" height="40" fill="none" stroke="#f0f0f0" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 60px; margin-left: 491px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">..</div></div></div></foreignObject><text x="520" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">..</text></switch></g><rect x="550" y="40" width="20" height="40" fill="none" stroke="#f0f0f0" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 560 60)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 60px; margin-left: 541px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span></div></div></div></foreignObject><text x="560" y="64" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">next</text></switch></g><rect x="70" y="120" width="60" height="40" fill="none" stroke="#ff55ff" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 140px; margin-left: 71px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">B</div></div></div></foreignObject><text x="100" y="144" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">B</text></switch></g><rect x="130" y="120" width="20" height="40" fill="none" stroke="#ff55ff" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 140 140)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 140px; margin-left: 121px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;"><span style="color: rgb(240, 240, 240); font-size: 12px; font-style: normal; font-variant-ligatures: normal; font-variant-caps: normal; font-weight: 400; letter-spacing: normal; orphans: 2; text-align: center; text-indent: 0px; text-transform: none; widows: 2; word-spacing: 0px; -webkit-text-stroke-width: 0px; background-color: rgb(27, 29, 30); text-decoration-thickness: initial; text-decoration-style: initial; text-decoration-color: initial; float: none; display: inline !important;">next</span></div></div></div></foreignObject><text x="140" y="144" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">next</text></switch></g><rect x="70" y="100" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 78px; height: 1px; padding-top: 110px; margin-left: 72px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">Thread 2</div></div></div></foreignObject><text x="72" y="114" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px">Thread 2</text></switch></g><rect x="210" y="120" width="60" height="40" fill="none" stroke="#55ffff" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 58px; height: 1px; padding-top: 140px; margin-left: 211px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">A</div></div></div></foreignObject><text x="240" y="144" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">A</text></switch></g><rect x="270" y="120" width="20" height="40" fill="none" stroke="#55ffff" pointer-events="all"/><g transform="translate(-0.5 -0.5)rotate(-90 280 140)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 38px; height: 1px; padding-top: 140px; margin-left: 261px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: center;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">next</div></div></div></foreignObject><text x="280" y="144" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px" text-anchor="middle">next</text></switch></g><rect x="210" y="100" width="80" height="20" fill="none" stroke="none" pointer-events="all"/><g transform="translate(-0.5 -0.5)"><switch><foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;"><div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe flex-start; width: 78px; height: 1px; padding-top: 110px; margin-left: 212px;"><div data-drawio-colors="color: rgb(240, 240, 240); " style="box-sizing: border-box; font-size: 0px; text-align: left;"><div style="display: inline-block; font-size: 12px; font-family: monospace; color: rgb(240, 240, 240); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">Thread 1</div></div></div></foreignObject><text x="212" y="114" fill="rgb(240, 240, 240)" font-family="monospace" font-size="12px">Thread 1</text></switch></g></g><switch><g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/><a transform="translate(0,-5)" xlink:href="https://www.drawio.com/doc/faq/svg-export-text-problems" target="_blank"><text text-anchor="middle" font-size="10px" x="50%" y="100%">Text is not SVG - cannot display</text></a></switch></svg>
\ No newline at end of file |