blob: f12231cefd22cc51c054c79b4da00a826906a6d7 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/**
* @author Xiao-Feng Li, 2006/10/25
*/
#ifndef _SYNC_STACK_H_
#define _SYNC_STACK_H_
#include "vector_block.h"
#define SYNC_STACK_VERSION_MASK_SHIFT 10
#define SYNC_STACK_VERSION_MASK ((1 << SYNC_STACK_VERSION_MASK_SHIFT) - 1)
typedef struct Node{
Node* next;
}Node;
/*
* ATTENTION: only for reference
* Perhaps in some platforms compilers compile this struct in a way different from what we expect
* GCC requires to specify "packed" attribute
#ifdef __linux__
typedef struct Stack_Top{
POINTER_SIZE_INT version: SYNC_STACK_VERSION_MASK_SHIFT;
POINTER_SIZE_INT entry: (BITS_OF_POINTER_SIZE_INT-SYNC_STACK_VERSION_MASK_SHIFT);
}Stack_Top __attribute__((packed));
#else
typedef struct Stack_Top{
POINTER_SIZE_INT version: SYNC_STACK_VERSION_MASK_SHIFT;
POINTER_SIZE_INT entry: (BITS_OF_POINTER_SIZE_INT-SYNC_STACK_VERSION_MASK_SHIFT);
}Stack_Top;
#endif
*/
typedef POINTER_SIZE_INT Stack_Top;
typedef struct Sync_Stack{
Stack_Top top; /* pointing to the first filled entry */
Node* cur; /* pointing to the current accessed entry, only for iterator */
}Sync_Stack;
#define stack_top_get_entry(top) ((Node*)((*(POINTER_SIZE_INT*)&(top)) & ~SYNC_STACK_VERSION_MASK))
/* The alternative way: (Node*)(top.entry<<SYNC_STACK_VERSION_MASK_SHIFT) */
#define stack_top_get_version(top) ((*(POINTER_SIZE_INT*)&(top)) & SYNC_STACK_VERSION_MASK)
/* The alternative way: (top.version) */
#define stack_top_contruct(entry, version) ((POINTER_SIZE_INT)(entry) | (version))
#define stack_top_get_next_version(top) ((stack_top_get_version(top) + 1) & SYNC_STACK_VERSION_MASK)
inline Sync_Stack* sync_stack_init()
{
unsigned int size = sizeof(Sync_Stack);
Sync_Stack* stack = (Sync_Stack*)STD_MALLOC(size);
memset(stack, 0, size);
stack->cur = NULL;
POINTER_SIZE_INT temp_top = 0;
stack->top = *(Stack_Top*)&temp_top;
return stack;
}
inline void sync_stack_destruct(Sync_Stack* stack)
{
STD_FREE(stack);
return;
}
inline void sync_stack_iterate_init(Sync_Stack* stack)
{
stack->cur = stack_top_get_entry(stack->top);
return;
}
inline Node* sync_stack_iterate_next(Sync_Stack* stack)
{
Node* entry = stack->cur;
while ( entry != NULL ){
Node* new_entry = entry->next;
Node* temp = (Node*)atomic_casptr((volatile void**)&stack->cur, new_entry, entry);
if(temp == entry){ /* got it */
return entry;
}
entry = stack->cur;
}
return NULL;
}
inline Node* sync_stack_pop(Sync_Stack* stack)
{
Stack_Top cur_top = stack->top;
Node* top_entry = stack_top_get_entry(cur_top);
POINTER_SIZE_INT version = stack_top_get_version(cur_top);
while( top_entry != NULL ){
POINTER_SIZE_INT temp = stack_top_contruct(top_entry->next, version);
temp = (POINTER_SIZE_INT)atomic_casptr((volatile void**)&stack->top, (void*)temp, (void*)cur_top);
if(temp == *(POINTER_SIZE_INT*)&cur_top){ // got it
top_entry->next = NULL;
return top_entry;
}
cur_top = stack->top;
top_entry = stack_top_get_entry(cur_top);
version = stack_top_get_version(cur_top);
}
return 0;
}
inline Boolean sync_stack_push(Sync_Stack* stack, Node* node)
{
Stack_Top cur_top = stack->top;
node->next = stack_top_get_entry(cur_top);
POINTER_SIZE_INT new_version = stack_top_get_next_version(cur_top);
POINTER_SIZE_INT temp = stack_top_contruct(node, new_version);
while( TRUE ){
temp = (POINTER_SIZE_INT)atomic_casptr((volatile void**)&stack->top, (void*)temp, (void*)cur_top);
if(temp == *(POINTER_SIZE_INT*)&cur_top){ // got it
return TRUE;
}
cur_top = stack->top;
node->next = stack_top_get_entry(cur_top);
new_version = stack_top_get_next_version(cur_top);
temp = stack_top_contruct(node, new_version);
}
// never comes here
return FALSE;
}
/* it does not matter whether this is atomic or not, because
it is only invoked when there is no contention or only for rough idea */
inline Boolean sync_stack_is_empty(Sync_Stack* stack)
{
return (stack_top_get_entry(stack->top) == NULL);
}
inline void sync_stack_empty(Sync_Stack* stack)
{
stack->top = (Stack_Top)NULL;
stack->cur = NULL;
}
inline unsigned int sync_stack_size(Sync_Stack* stack)
{
unsigned int entry_count = 0;
sync_stack_iterate_init(stack);
while(sync_stack_iterate_next(stack)){
++entry_count;
}
return entry_count;
}
#endif /* _SYNC_STACK_H_ */