diff --git a/spec/1-design-goals.md b/spec/1-design-goals.md
index acb0d85be..2b09118c0 100644
--- a/spec/1-design-goals.md
+++ b/spec/1-design-goals.md
@@ -25,6 +25,14 @@
Chronicle Map provides some guarantees under the following assumptions:
+#### Threads
+
+Throughout this specification, the term *thread* means the execution thread of the runtime of the
+Chronicle Map implementation. It might be an OS thread or a "greener" thread.
+
+ - Operations declared for execution in some threads are performed sequentially in the same order,
+ with full memory visibility between operations.
+
#### CPU
- CPU supports atomic 64-bit compare-and-swap operations with aligned memory, i. e. if several
@@ -36,6 +44,8 @@ Chronicle Map provides some guarantees under the following assumptions:
The two above points are true for CPUs with x86 and x86_64 architectures.
+#### Memory ordering
+
- If some values Xi are written at addresses Ai, then 32- or 64-bit value Y
is written at address B, there is a way to ensure some memory order. In particular, if a
concurrent thread reads value Y from address B, after that it will read Xi The read level couldn't upgrade to the higher levels, because this is dead-lock prone. Two threads
> could acquire the same lock on the read level, then both try to upgrade to the write level - and
@@ -46,8 +46,8 @@ simple value reading by the key. This level couldn't be upgraded to the update o
> this issue, too.)
The **update level** is partially exclusive: a thread could hold the segment lock on the update
-level while some other threads or processes are holding the same lock on the read level, but not
-the update or write level.
+level while some other threads (across OS processes) are holding the same lock on the read level,
+but not the update or write level.
This level is used for "typically read" types of queries, which occasionally should be able to write
to the data store, hence need to upgrade to the write level without releasing the lock, for example,
@@ -58,10 +58,10 @@ level, even if they never perform writes.
A segment lock acquired on the update level could be upgraded to the write level.
The **write level** is totally exclusive: when a thread holds the segment lock on the write level,
-no other thread or process could hold the same lock on any level. The write level is used for write
-queries.
+no other thread (across OS processes) could hold the same lock on any level. The write level is used
+for write queries.
-## Lock implementation
+## [Lock implementation](3_2-lock-structure.md)
Each lock is represented by two 32-bit words in the Chronicle Map memory. *Count word* holds the
numbers of threads holding the lock at the moment on the read, update and write levels. *Wait word*
@@ -77,8 +77,8 @@ and conditional wake-ups. Threads acquire locks in a spin loop, yielding and/or
certain threshold numbers of unsuccessful attempts.
> Chronicle Map implementations specialized for single-process access to instances may implement
-> some kind of thread queues and wake-ups on their runtime level; certain lock acquisition strategy
-> is out of scope of the Chronicle Map data store specification.
+> some kind of thread queues, wake-ups and cooperative scheduling on the runtime level; certain lock
+> acquisition strategy is out of scope of the Chronicle Map data store specification.
## The segment data structure
diff --git a/spec/3-memory-layout.md b/spec/3-memory-layout.md
index 00e89cf7f..7a7ac9573 100644
--- a/spec/3-memory-layout.md
+++ b/spec/3-memory-layout.md
@@ -254,16 +254,16 @@ The segment tier counters structure is 64 bytes long:
#### Free list
-A free list starts with offset [`tierHashLookupOuterSize`
-](3_1-header-fields.md#tierhashlookupoutersize) + 64 from the beginning address of a containing
+A free list starts with offset [`tierHashLookupOuterSize`](
+3_1-header-fields.md#tierhashlookupoutersize) + 64 from the beginning address of a containing
segment tier.
This is a list of [`actualChunksPerSegmentTier`](3_1-header-fields.md#actualchunkspersegmenttier)
bits, each corresponding to a chunk in the [entry space](#entry-space). If the chunk is allocated
for some entry, the corresponding bit is set to 1, otherwise it is clear.
-> The reference Java implementation: [`SingleThreadedFlatBitSetFrame`
-> ](https://github.com/OpenHFT/Chronicle-Algorithms/blob/chronicle-algorithms-1.1.6/src/main/java/net/openhft/chronicle/algo/bitset/SingleThreadedFlatBitSetFrame.java).
+> The reference Java implementation: [`SingleThreadedFlatBitSetFrame`](
+> https://github.com/OpenHFT/Chronicle-Algorithms/blob/chronicle-algorithms-1.1.6/src/main/java/net/openhft/chronicle/algo/bitset/SingleThreadedFlatBitSetFrame.java).
#### Entry space
diff --git a/spec/3_2-lock-structure.md b/spec/3_2-lock-structure.md
index 4951fd715..9d1730e93 100644
--- a/spec/3_2-lock-structure.md
+++ b/spec/3_2-lock-structure.md
@@ -55,14 +55,21 @@ The lock structure is 8 bytes (64 bits) long.
Attempt to acquire the read or update lock using the corresponding *try acquire* procedure, until it
succeeds, optionally inserting some directives aimed to yield the resources after failed attempts,
-for example, x86's `PAUSE` instruction, or an operation system command to schedule another thread to
-the current CPU, or a command to clock down the current CPU, etc.
+for example, x86's `PAUSE` instruction, or an OS command to schedule another OS thread to the
+current CPU, or a runtime command to schedule another green thread to the current OS thread, or a
+command to clock down the current CPU, etc.
+
+If the runtime of the Chronicle Map implementation has cooperative (rather than preemptive)
+scheduling of [threads](1-design-goals.md#threads), thread switch must be enabled after failed
+attempts. Some explicit command is executed, letting the runtime to switch between green threads.
+
+> Possibility for thread switch is required to avoid dead locks between threads.
There is no single exact scheme that implementations should follow, it is unspecified. The
implementation may vary for the read and update locks as well.
> The reference Java implementation doesn't insert any resources yielding directives after failed
-> try acquire attempts, for both read and update lock acquisition.
+> acquisition attempts, for both read and update lock acquisition.
Implementations must not try to acquire the read or update lock indefinitely, after some finite
number of failed attempts or some finite time elapsed since the time-limited lock acquisition
@@ -142,16 +149,23 @@ procedure.
procedure fails.
3. Read the lock state.
4. If the count word of the lock state is not equal to 0 (in case of acquiring the write lock) or
- 0x40000000 (in case of upgrading update to write lock), go to the step 7.
+ 0x40000000 (in case of upgrading update to write lock), go to the 7th step.
5. If the wait count is 0, this procedure fails.
- 6. Perform a CAS operation on the whole lock structure, comparing the state, read on the step 3,
+ 6. Perform a CAS operation on the whole lock structure, comparing the state, read on the 3rd step,
with an updated state with the wait count decremented, and count word of 0x80000000 (the write lock
flag set). If the CAS operation succeeds, the time-limited acquisition succeeds. If the CAS
- operation fails, continue with the step 7.
+ operation fails, continue with the 7th step.
7. Check if this time-limited procedure has run out of the time limit or the number of attempts.
If it is, the time-limited acquisition fails, [deregister a wait](#deregister-wait) before exiting
from this procedure (the result of the deregister wait procedure doesn't matter). Otherwise,
- continue from the step 3 of this procedure.
+ continue from the 3rd step of this procedure, optionally performing some directives aimed to yield
+ the resources at first, as described in the section about [time-limited read or update lock
+ acquisition](#time-limited-read-or-update-lock-acquisition).
+
+ If the runtime of the Chronicle Map implementation has cooperative (rather than preemptive)
+ scheduling of [threads](1-design-goals.md#threads), thread switch must be enabled before continuing
+ with the 3rd step. Some explicit command is executed, letting the runtime to switch between green
+ threads.
Implementations doesn't make locking attempts indefinitely [for the same reasons, as in case of
acquiring read and update lock](#time-limited-read-or-update-lock-acquisition).
diff --git a/spec/5-initialization.md b/spec/5-initialization.md
index db77d499c..11c5e0396 100644
--- a/spec/5-initialization.md
+++ b/spec/5-initialization.md
@@ -28,9 +28,9 @@ existing Map, persisted to some file).
> This step follows [double-checked locking](https://en.wikipedia.org/wiki/Double-checked_locking)
> pattern to avoid file locking in most cases when an already existing Chronicle Map is opened.
- This step is serialized for the persistence file among threads within the current process, because
- file locking mechanism in operating systems is per-process, not per-thread. I. e. concurrent
- threads within the same process don't lock the same file at the same time.
+ This step is serialized for the persistence file among threads within the current OS process,
+ because file locking mechanism in operating systems is per-process, not per-thread. I. e.
+ concurrent threads within the same OS process don't lock the same file at the same time.
Concurrent threads perform any initialization operations (Chronicle Map creation and [extra tier
bulk allocation](#extra-tier-bulk-allocation)) with canonicalized file descriptor, that is closed
@@ -59,8 +59,8 @@ existing Map, persisted to some file).
bit of the 32-bit word by offset 8 from the beginning of the file, read and written in the
little-endian order. Read the [Size Prefix Blob](
https://github.com/OpenHFT/RFC/blob/master/Size-Prefixed-Blob/Size-Prefixed-Blob-0.1.md)
- specification for details. This step is the event threads and processes [waiting until Chronicle
- Map is ready](#wait-until-chronicle-map-is-ready) actually wait for.
+ specification for details. This step is the event that is actually awaited by the threads (across
+ OS processes) [waiting until Chronicle Map is ready](#wait-until-chronicle-map-is-ready).
### Wait until Chronicle Map is ready
@@ -68,7 +68,7 @@ Check until the readiness bit is set to 0. The readiness bit it the highest bit
offset 8 from the beginning of the Chronicle Map persistence file, read in the little-endian order.
Optionally yield processor resources after failed checks, e. g. make the waiting thread sleep for
-some time. The exact yielding operation (or if it is making the current process sleeping, the exact
+some time. The exact yielding operation (or if it is making the current thread sleeping, the exact
sleep interval) is unspecified.
> The reference Java implementations turns the waiting thread to sleep for 100 ms after failed
@@ -77,8 +77,7 @@ sleep interval) is unspecified.
Implementations must not wait until the Chronicle Map is ready indefinitely, after some finite
number of failed checks, or some finite time elapsed since the start, the wait procedure must fail.
-> Indefinite wait is dead lock prone, if the concurrent process or thread doing the initialization
-> fails itself.
+> Indefinite wait is dead lock prone, if the thread doing the initialization fails itself.
> The reference Java implementation checks until the Chronicle Map is ready for approximately 1
> minute, then throws an exception.