Sliding window is an important data structure of log module, used for log sending and receiving and log index query, and the group of students discussed the multi-threaded security sliding window design, there are three kinds of implementation scheme, write this document record.
1. Interface description
The sliding window internally uses an array, and each array item is a struct:
Structentry
{
Struct Valuenode *head;
Struct Valuenode *tail;
int64_t CNT;
Int64_tstat;
}
Since different values are written multiple times for the same item, multiple values written will be organized in a linked list, head pointing to the chain header, tail pointing at the end of the chain, and CNT indicates reading the current entry reference count, including all nodes in the linked list.
Structvaluenode is defined as follows:
Structvaluenode
{
Void *value
Struct Valuenode *next;
}
The following interfaces are required for sliding windows:
1. Init (int64-_t size)
Initializes the sliding window, which is used to indicate the size of the sliding window.
2. Set (int64-_t ID, const void*val)
The set interface is used to write data to the sliding window, and the ID is used to indicate the ordinal of the data being written, and Val points to the data pointer being written. Overwrite occurs when different values are inserted for the same ID.
3. Get (int64-_t ID, void* &val)
The Get interface is used to read data from a sliding window, and the ID is used to indicate the ordinal of the data being read, and Val points to a pointer to the data being read.
4. Revert (int64-_t ID)
The revert interface needs to be called when reading an item at the end.
5. Move_foward ()
Move_foward is used to move the sliding window forward, for the removal of the sliding window of the item, you need to call its revert interface, reset the entry to facilitate subsequent reuse of this interface.
2 Scheme one: read-write lock protection start_id
Scenario one is a relatively low concurrency, but the idea of a simple implementation scenario, in this scenario, the sliding window needs to maintain the member variables:
1. Size: This variable is used to indicate the size of the sliding window;
2. START_ID: This variable is used to indicate the smallest ID in the sliding window;
3. END_ID: This variable is used to indicate the maximum ID in the sliding window;
4. Rw_lock: For the protection of start_id;
Interface Implementation Description:
1. Init (int64_t size):
Log the size to the member variable, and request the array memory (size is sized), set the start_id end_id to 0;
2. Set (int64_t ID, const void*val)
1) Read the Rw_lock lock;
2) determine whether to meet start_id <= ID <start_id + size, if not met jump to step 4;
3) Use ID to modulo size, find the corresponding entry, read this entry tail pointer, according to the implementation of the registration of the judgment function, determine whether can overwrite the write, if not can jump to step 4; If you can overwrite the write or the tail pointer is empty, create a new Valuenode , append it to the tail of the list and modifies the tail pointer and increments the CNT reference count (there are multi-threaded concurrency problems, you can use CAS128);
4) Unlock the Rw_lock.
3. Get (int64_t ID, void* &val)
1) determine whether to meet start_id <= ID <start_id + size, if not met jump to step 4;
2) record the current start_id to the temporary variable tmp_start_id;
3) According to the ID modulo, find the corresponding entry, if cnt = = 0, jump step 7;
4) Increment the CNT reference count and assign the Vallue value of the tail pointer to Val;
5) Read the current start_id and tmp_start_id comparison, if not equal, then jump step 1;
6) return;
4. Revert (int64-_t ID)
1) According to the ID found should be entry, if CNT ==0, error;
2) Decrement CNT, if decreasing after more than 0, then exit;
3) If the reduced CNT ==0, traverse the linked list that the head points to, call each value's revert function, and release each valuenode memory;
5. Move_forwad ()
1) Call the Get interface, read the state of the start_id position, according to the registered function, determine whether it can be moved out of the sliding window, if not can be returned;
2) Rw_lock write lock, starting from start_id, scan sliding window, for each entry take the following actions:
A) According to the registered function to determine whether you can move out of the sliding window, if not, jump to step 3;
b) If possible, decrements its reference count, if it is not 0 after descending, it needs to block wait;
c) Increment the start_id by 1;
3) Unlock the Rw_lock and return the function.
3. Programme II: No Lock (i)
In scenario one, the read-write lock affects the concurrency to a certain extent, and scenario two introduces a thread-safe implementation that can be used without a read-write lock. It should be added that scenario two requires the addition of a stat field in the entry, and Stat has three optional values:
1. NULL
Indicates that this entry is not used and can write data;
2. Use
Indicates that this entry is being used and can be read;
3. LOCKED
Indicates that this entry is in the middle state of null and use, unreadable and not writable;
Also, the sliding window needs to maintain the member variable last_start_id, which represents the entry between the last start_id,last_start_id and start during a move_forward () process, which is required to call reset () Cleared away.
On these basis, the interface is described as follows:
1. Get ():
Consistent with Scenario one
2. Revert ():
Consistent with the programme.
3. Set ():
1) determine whether to meet start_id <= ID < last_start_id+ size, if not satisfied then jump to step 5;
2) using ID to modulo size, find the corresponding entry, read the status of this entry, if it is locked, then jump Step 1;
3) Modify the entry state to locked, and again determine if the start_id <= ID <last_start_id+ size is satisfied, and if not, modify the entry state back to the original state and jump to step 5;
4) read start tail pointer, according to the implementation of the registration of the judgment function, determine whether can overwrite the write, if not can jump to step 4; If you can overwrite the write or the tail pointer is empty, create a new Valuenode, append it to the tail of the list, and modify the tail pointer and increment the CNT reference count (there are multi-threaded concurrency problems, you can use CAS128); Modify the entry state to be used;
5) return
4. Move_foward ()
1) first obtain the target ID to move the starting point of the sliding window back, and record it as Target_start;
2) record the start_id to the TEMP variable tmp_start and then modify start to Target_start, which needs to be done in an atomic operation
3) For each entry in the middle of Tmp_start to Target_start, do the following:
A) to determine whether its stat is locked, if it is blocking wait;
b) Change stat to locked;
c) decrements its reference count, if not 0 after decrement, it needs to block wait, if 0, frees its memory and sets its state to null;
4) Compare the size relationships between last_start_id and Tmp_start, and if they are equal, modify Last_start to Target_start, otherwise block wait.
In this scheme, in fact, by adding a state value to each entry, the modification of each entry to do concurrency control, relative to the scheme one, reducing the lock granularity.
In the fourth step of the Move_foward () interface implementation, the size relationship between Last_start and Tmp_start is compared, in fact, to ensure that when multiple threads simultaneously invoke the Move_foward () interface while modifying last_start_id, Be able to do serialization, that is, to ensure last_start_id order increment modification.
4. Programme II: No Lock (ii)
The second type of lock-free implementation is described below, where maintenance of last_log_id is not required, but the entry state is maintained.
On these basis, the interface is described as follows:
1. Get ():
It is basically consistent with the scheme, but it is necessary to judge the entry state of the read, and if it is locked, it needs to return to step one to re-judge.
2. Revert ():
Consistent with the programme.
3. Set ():
1) determine whether to meet start_id <= ID <start_id + size, if not met jump to step 5;
2) using ID to modulo size, find the corresponding entry, read the status of this entry, if it is locked, then jump Step 1;
3) Change the entry state to locked, and again determine whether the start_id <= id<start_id + size: If not satisfied, the entry state will be modified back to the original state, and jump to step 5;
4) read start tail pointer, according to the implementation of the registration of the judgment function, determine whether can overwrite the write, if not can jump to step 5; If you can overwrite the write or the tail pointer is empty, create a new Valuenode, append it to the tail of the list, and modify the tail pointer and increment the CNT reference count (there are multi-threaded concurrency problems, you can use CAS128); Modify the entry state to be used;
5) return.
4. Move_foward ()
1) first obtain the target ID to move the starting point of the sliding window back, and record it as Target_start;
2) record the start_id to a temporary variable, Tmp_start, for each entry in the middle of start to Target_start, do the following:
A) to determine whether its stat is locked, if it is blocking wait;
b) Change stat to locked, re-read start, determine whether start_id equals Tmp_start, if not, then jump to step 2 to start;
c) decrements its reference count, which, if not 0 after descending, needs to block the wait, and if 0, frees its memory and sets its state to null;
d) increment the start_id by 1;
4. Special Needs:
In our design, the newly-elected leader needs to write a sync barrier log before it can process the pending logs in the sliding window. If the normal pending log in the sliding window is already full, the Sync barrier log can no longer be written, causing the recovery process to fail.
As a result, sliding windows need to provide a special interface: Set_common_entry () and Set_special_entry (), while initialization requires incoming common_size and special_size, usually special_size >common_size. Two different interfaces use different size to ensure that the Sync_barrier log can be written to a sliding window.
Design and implementation of multi-thread secure sliding window