第四章 并发与同步¶
4.1 并发¶
4.2 同步¶
/*
* File: typedef.h
*/
#include <stdio.h>
#ifndef TYPEDEF_H
#define TYPEDEF_H
typedef enum _Ret
{
RET_OK,
RET_OOM,
RET_STOP,
RET_INVALID_PARAMS,
RET_FAIL
}Ret;
#ifdef __cplusplus
#define DECLS_BEGIN extern "C" {
#define DECLS_END }
#else
#define DECLS_BEGIN
#define DECLS_END
#endif/*__cplusplus*/
#define return_if_fail(p) if(!(p)) \
{printf("%s:%d Warning: "#p" failed.\n", \
__func__, __LINE__); return;}
#define return_val_if_fail(p, ret) if(!(p)) \
{printf("%s:%d Warning: "#p" failed.\n",\
__func__, __LINE__); return (ret);}
#define SAFE_FREE(p) if(p != NULL) {free(p); p = NULL;}
#endif/*TYPEDEF_H*/
/*
* File: locker
*/
#include "typedef.h"
#ifndef LOCKER_H
#define LOCKER_H
DECLS_BEGIN
struct _Locker;
typedef struct _Locker Locker;
typedef Ret (*LockerLockFunc)(Locker* thiz);
typedef Ret (*LockerUnlockFunc)(Locker* thiz);
typedef void (*LockerDestroyFunc)(Locker* thiz);
struct _Locker
{
LockerLockFunc lock;
LockerUnlockFunc unlock;
LockerDestroyFunc destroy;
char priv[0];
};
static inline Ret locker_lock(Locker* thiz)
{
return_val_if_fail(thiz != NULL && thiz->lock != NULL, RET_INVALID_PARAMS);
return thiz->lock(thiz);
}
static inline Ret locker_unlock(Locker* thiz)
{
return_val_if_fail(thiz != NULL && thiz->unlock != NULL, RET_INVALID_PARAMS);
return thiz->unlock(thiz);
}
static inline void locker_destroy(Locker* thiz)
{
return_if_fail(thiz != NULL && thiz->destroy != NULL);
thiz->destroy(thiz);
return;
}
DECLS_END
#endif/*LOCKER_H*/
/*
* File: locker_pthread.h
*/
#include "locker.h"
#ifndef LOCKER_PTHREAD_H
#define LOCKER_PTHREAD_H
DECLS_BEGIN
Locker* locker_pthread_create(void);
DECLS_END
#endif/*LOCKER_PTHREAD_H*/
/*
* File: locker_pthread.c
*/
#include <stdlib.h>
#include <pthread.h>
#include "locker_pthread.h"
typedef struct _PrivInfo
{
pthread_mutex_t mutex;
}PrivInfo;
static Ret locker_pthread_lock(Locker* thiz)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
int ret = pthread_mutex_lock(&priv->mutex);
return ret == 0 ? RET_OK : RET_FAIL;
}
static Ret locker_pthread_unlock(Locker* thiz)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
int ret = pthread_mutex_unlock(&priv->mutex);
return ret == 0 ? RET_OK : RET_FAIL;
}
static void locker_pthread_destroy(Locker* thiz)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
int ret = pthread_mutex_destroy(&priv->mutex);
SAFE_FREE(thiz);
return;
}
Locker* locker_pthread_create(void)
{
Locker* thiz = (Locker*)malloc(sizeof(Locker) + sizeof(PrivInfo));
if(thiz != NULL)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
thiz->lock = locker_pthread_lock;
thiz->unlock = locker_pthread_unlock;
thiz->destroy = locker_pthread_destroy;
pthread_mutex_init(&(priv->mutex), NULL);
}
return thiz;
}
/*
* File: dlist.h
*/
#include <stdio.h>
#include "locker.h"
#ifndef DLIST_H
#define DLIST_H
DECLS_BEGIN
struct _DList;
typedef struct _DList DList;
typedef void (*DListDataDestroyFunc)(void* ctx, void* data);
typedef int (*DListDataCompareFunc)(void* ctx, void* data);
typedef Ret (*DListDataVisitFunc)(void* ctx, void* data);
DList* dlist_create(DListDataDestroyFunc data_destroy, void* ctx, Locker* locker);
Ret dlist_insert(DList* thiz, size_t index, void* data);
Ret dlist_prepend(DList* thiz, void* data);
Ret dlist_append(DList* thiz, void* data);
Ret dlist_delete(DList* thiz, size_t index);
Ret dlist_get_by_index(DList* thiz, size_t index, void** data);
Ret dlist_set_by_index(DList* thiz, size_t index, void* data);
size_t dlist_length(DList* thiz);
int dlist_find(DList* thiz, DListDataCompareFunc cmp, void* ctx);
Ret dlist_foreach(DList* thiz, DListDataVisitFunc visit, void* ctx);
void dlist_destroy(DList* thiz);
DECLS_END
#endif/*DLIST*/
/*
* File: dlist.c
*/
#include <stdlib.h>
#include <unistd.h>
#include "dlist.h"
typedef struct _DListNode
{
struct _DListNode* prev;
struct _DListNode* next;
void* data;
}DListNode;
struct _DList
{
Locker* locker;
DListNode* first;
void* data_destroy_ctx;
DListDataDestroyFunc data_destroy;
};
static void dlist_destroy_data(DList* thiz, void* data)
{
if(thiz->data_destroy != NULL)
{
thiz->data_destroy(thiz->data_destroy_ctx, data);
}
return;
}
static DListNode* dlist_create_node(DList* thiz, void* data)
{
DListNode* node = malloc(sizeof(DListNode));
if(node != NULL)
{
node->prev = NULL;
node->next = NULL;
node->data = data;
}
return node;
}
static void dlist_destroy_node(DList* thiz, DListNode* node)
{
if(node != NULL)
{
node->next = NULL;
node->prev = NULL;
dlist_destroy_data(thiz, node->data);
SAFE_FREE(node);
}
return;
}
static void dlist_lock(DList* thiz)
{
if(thiz->locker != NULL)
{
locker_lock(thiz->locker);
}
return;
}
static void dlist_unlock(DList* thiz)
{
if(thiz->locker != NULL)
{
locker_unlock(thiz->locker);
}
return;
}
static void dlist_destroy_locker(DList* thiz)
{
if(thiz->locker != NULL)
{
locker_unlock(thiz->locker);
locker_destroy(thiz->locker);
}
return;
}
DList* dlist_create(DListDataDestroyFunc data_destroy, void* ctx, Locker* locker)
{
DList* thiz = malloc(sizeof(DList));
if(thiz != NULL)
{
thiz->first = NULL;
thiz->locker = locker;
thiz->data_destroy = data_destroy;
thiz->data_destroy_ctx = ctx;
}
return thiz;
}
static DListNode* dlist_get_node(DList* thiz, size_t index, int fail_return_last)
{
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL, NULL);
iter = thiz->first;
while(iter != NULL && iter->next != NULL && index > 0)
{
iter = iter->next;
index--;
}
if(!fail_return_last)
{
iter = index > 0 ? NULL : iter;
}
return iter;
}
Ret dlist_insert(DList* thiz, size_t index, void* data)
{
Ret ret = RET_OK;
DListNode* node = NULL;
DListNode* cursor = NULL;
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
dlist_lock(thiz);
do
{
if((node = dlist_create_node(thiz, data)) == NULL)
{
ret = RET_OOM;
break;
}
if(thiz->first == NULL)
{
thiz->first = node;
break;
}
cursor = dlist_get_node(thiz, index, 1);
if(index < dlist_length(thiz))
{
node->next = cursor;
if(cursor->prev != NULL)
{
cursor->prev->next = node;
}
cursor->prev = node;
if(thiz->first == cursor)
{
thiz->first = node;
}
}
else
{
cursor->next = node;
node->prev = cursor;
}
}while(0);
dlist_unlock(thiz);
return ret;
}
Ret dlist_prepend(DList* thiz, void* data)
{
return dlist_insert(thiz, 0, data);
}
Ret dlist_append(DList* thiz, void* data)
{
return dlist_insert(thiz, -1, data);
}
Ret dlist_delete(DList* thiz, size_t index)
{
Ret ret = RET_OK;
DListNode* cursor = NULL;
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
dlist_lock(thiz);
cursor = dlist_get_node(thiz, index, 0);
do
{
if(cursor == NULL)
{
ret = RET_INVALID_PARAMS;
break;
}
if(cursor != NULL)
{
if(cursor == thiz->first)
{
thiz->first = cursor->next;
}
if(cursor->next != NULL)
{
cursor->next->prev = cursor->prev;
}
if(cursor->prev != NULL)
{
cursor->prev->next = cursor->next;
}
dlist_destroy_node(thiz, cursor);
}
}while(0);
dlist_unlock(thiz);
return RET_OK;
}
Ret dlist_get_by_index(DList* thiz, size_t index, void** data)
{
DListNode* cursor = NULL;
return_val_if_fail(thiz != NULL && data != NULL, RET_INVALID_PARAMS);
dlist_lock(thiz);
cursor = dlist_get_node(thiz, index, 0);
if(cursor != NULL)
{
*data = cursor->data;
}
dlist_unlock(thiz);
return cursor != NULL ? RET_OK : RET_INVALID_PARAMS;
}
Ret dlist_set_by_index(DList* thiz, size_t index, void* data)
{
DListNode* cursor = NULL;
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
dlist_lock(thiz);
cursor = dlist_get_node(thiz, index, 0);
if(cursor != NULL)
{
cursor->data = data;
}
dlist_unlock(thiz);
return cursor != NULL ? RET_OK : RET_INVALID_PARAMS;
}
size_t dlist_length(DList* thiz)
{
size_t length = 0;
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL, 0);
dlist_lock(thiz);
iter = thiz->first;
while(iter != NULL)
{
length++;
iter = iter->next;
}
dlist_unlock(thiz);
return length;
}
Ret dlist_foreach(DList* thiz, DListDataVisitFunc visit, void* ctx)
{
Ret ret = RET_OK;
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL && visit != NULL, RET_INVALID_PARAMS);
dlist_lock(thiz);
iter = thiz->first;
while(iter != NULL && ret != RET_STOP)
{
ret = visit(ctx, iter->data);
iter = iter->next;
}
dlist_unlock(thiz);
return ret;
}
int dlist_find(DList* thiz, DListDataCompareFunc cmp, void* ctx)
{
int i = 0;
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL && cmp != NULL, -1);
dlist_lock(thiz);
iter = thiz->first;
while(iter != NULL)
{
if(cmp(ctx, iter->data) == 0)
{
break;
}
i++;
iter = iter->next;
}
dlist_unlock(thiz);
return i;
}
void dlist_destroy(DList* thiz)
{
DListNode* iter = NULL;
DListNode* next = NULL;
return_if_fail(thiz != NULL);
dlist_lock(thiz);
iter = thiz->first;
while(iter != NULL)
{
next = iter->next;
dlist_destroy_node(thiz, iter);
iter = next;
}
thiz->first = NULL;
dlist_destroy_locker(thiz);
SAFE_FREE(thiz);
return;
}
#ifdef DLIST_TEST
#include <assert.h>
#include <pthread.h>
#include "locker_pthread.h"
static int cmp_int(void* ctx, void* data)
{
return (int)data - (int)ctx;
}
static Ret print_int(void* ctx, void* data)
{
printf("%d ", (int)data);
return RET_OK;
}
static Ret check_and_dec_int(void* ctx, void* data)
{
int* expected =(int*)ctx;
assert(*expected == (int)data);
(*expected)--;
return RET_OK;
}
static void test_int_dlist(void)
{
int i = 0;
int n = 100;
int data = 0;
DList* dlist = dlist_create(NULL, NULL, NULL);
for(i = 0; i < n; i++)
{
assert(dlist_append(dlist, (void*)i) == RET_OK);
assert(dlist_length(dlist) == (i + 1));
assert(dlist_get_by_index(dlist, i, (void**)&data) == RET_OK);
assert(data == i);
assert(dlist_set_by_index(dlist, i, (void*)(2*i)) == RET_OK);
assert(dlist_get_by_index(dlist, i, (void**)&data) == RET_OK);
assert(data == 2*i);
assert(dlist_set_by_index(dlist, i, (void*)i) == RET_OK);
assert(dlist_find(dlist, cmp_int, (void*)i) == i);
}
for(i = 0; i < n; i++)
{
assert(dlist_get_by_index(dlist, 0, (void**)&data) == RET_OK);
assert(data == (i));
assert(dlist_length(dlist) == (n-i));
assert(dlist_delete(dlist, 0) == RET_OK);
assert(dlist_length(dlist) == (n-i-1));
if((i + 1) < n)
{
assert(dlist_get_by_index(dlist, 0, (void**)&data) == RET_OK);
assert((int)data == (i+1));
}
}
assert(dlist_length(dlist) == 0);
for(i = 0; i < n; i++)
{
assert(dlist_prepend(dlist, (void*)i) == RET_OK);
assert(dlist_length(dlist) == (i + 1));
assert(dlist_get_by_index(dlist, 0, (void**)&data) == RET_OK);
assert(data == i);
assert(dlist_set_by_index(dlist, 0, (void*)(2*i)) == RET_OK);
assert(dlist_get_by_index(dlist, 0, (void**)&data) == RET_OK);
assert(data == 2*i);
assert(dlist_set_by_index(dlist, 0, (void*)i) == RET_OK);
}
i = n - 1;
assert(dlist_foreach(dlist, check_and_dec_int, &i) == RET_OK);
dlist_destroy(dlist);
return;
}
static void test_invalid_params(void)
{
printf("===========Warning is normal begin==============\n");
assert(dlist_length(NULL) == 0);
assert(dlist_prepend(NULL, 0) == RET_INVALID_PARAMS);
assert(dlist_append(NULL, 0) == RET_INVALID_PARAMS);
assert(dlist_delete(NULL, 0) == RET_INVALID_PARAMS);
assert(dlist_insert(NULL, 0, 0) == RET_INVALID_PARAMS);
assert(dlist_set_by_index(NULL, 0, 0) == RET_INVALID_PARAMS);
assert(dlist_get_by_index(NULL, 0, NULL) == RET_INVALID_PARAMS);
assert(dlist_find(NULL, NULL, NULL) < 0);
assert(dlist_foreach(NULL, NULL, NULL) == RET_INVALID_PARAMS);
printf("===========Warning is normal end==============\n");
return;
}
static void single_thread_test(void)
{
test_int_dlist();
test_invalid_params();
return;
}
static void* producer(void* param)
{
int i = 0;
DList* dlist = (DList*)param;
for(i = 0; i < 100; i++)
{
assert(dlist_append(dlist, (void*)i) == RET_OK);
}
sleep(1);
for(i = 0; i < 100; i++)
{
assert(dlist_prepend(dlist, (void*)i) == RET_OK);
}
return NULL;
}
static void* consumer(void* param)
{
int i = 0;
DList* dlist = (DList*)param;
for(i = 0; i < 200; i++)
{
usleep(20);
assert(dlist_delete(dlist, 0) == RET_OK);
}
return NULL;
}
static void multi_thread_test(void)
{
pthread_t consumer_tid = 0;
pthread_t producer_tid = 0;
DList* dlist = dlist_create(NULL, NULL, locker_pthread_create());
pthread_create(&producer_tid, NULL, producer, dlist);
pthread_create(&consumer_tid, NULL, consumer, dlist);
pthread_join(consumer_tid, NULL);
pthread_join(producer_tid, NULL);
return;
}
int main(int argc, char* argv[])
{
single_thread_test();
multi_thread_test();
return 0;
}
#endif
CFILES=dlist.c locker_pthread.c
all:
#gcc -g -m32 -fPIC -shared -lpthread $(CFILES) -o libdlist.so
gcc -g -m32 -shared -lpthread $(CFILES) -o libdlist.so
gcc -g -m32 -DDLIST_TEST $(CFILES) -o dlist_test -lpthread
clean:
rm -f *test *.exe *.so
4.3 嵌套锁与装饰模式¶
/*
* File: typedef.h
*/
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#ifndef TYPEDEF_H
#define TYPEDEF_H
typedef enum _Ret
{
RET_OK,
RET_OOM,
RET_STOP,
RET_INVALID_PARAMS,
RET_FAIL
}Ret;
#ifdef __cplusplus
#define DECLS_BEGIN extern "C" {
#define DECLS_END }
#else
#define DECLS_BEGIN
#define DECLS_END
#endif/*__cplusplus*/
#define return_if_fail(p) if(!(p)) \
{printf("%s:%d Warning: "#p" failed.\n", \
__func__, __LINE__); return;}
#define return_val_if_fail(p, ret) if(!(p)) \
{printf("%s:%d Warning: "#p" failed.\n",\
__func__, __LINE__); return (ret);}
#define SAFE_FREE(p) if(p != NULL) {free(p); p = NULL;}
#endif/*TYPEDEF_H*/
/*
* File: locker.h
*/
#include "typedef.h"
#ifndef LOCKER_H
#define LOCKER_H
DECLS_BEGIN
struct _Locker;
typedef struct _Locker Locker;
typedef Ret (*LockerLockFunc)(Locker* thiz);
typedef Ret (*LockerUnlockFunc)(Locker* thiz);
typedef void (*LockerDestroyFunc)(Locker* thiz);
struct _Locker
{
LockerLockFunc lock;
LockerUnlockFunc unlock;
LockerDestroyFunc destroy;
char priv[0];
};
static inline Ret locker_lock(Locker* thiz)
{
return_val_if_fail(thiz != NULL && thiz->lock != NULL, RET_INVALID_PARAMS);
return thiz->lock(thiz);
}
static inline Ret locker_unlock(Locker* thiz)
{
return_val_if_fail(thiz != NULL && thiz->unlock != NULL, RET_INVALID_PARAMS);
return thiz->unlock(thiz);
}
static inline void locker_destroy(Locker* thiz)
{
return_if_fail(thiz != NULL && thiz->destroy != NULL);
thiz->destroy(thiz);
return;
}
DECLS_END
#endif/*LOCKER_H*/
/*
* File: locker_pthread.h
*/
#include "locker.h"
#ifndef LOCKER_PTHREAD_H
#define LOCKER_PTHREAD_H
DECLS_BEGIN
Locker* locker_pthread_create(void);
DECLS_END
#endif/*LOCKER_PTHREAD_H*/
/*
* File: locker_pthread.c
*/
#include <stdlib.h>
#include <pthread.h>
#include "locker_pthread.h"
typedef struct _PrivInfo
{
pthread_mutex_t mutex;
}PrivInfo;
static Ret locker_pthread_lock(Locker* thiz)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
int ret = pthread_mutex_lock(&priv->mutex);
return ret == 0 ? RET_OK : RET_FAIL;
}
static Ret locker_pthread_unlock(Locker* thiz)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
int ret = pthread_mutex_unlock(&priv->mutex);
return ret == 0 ? RET_OK : RET_FAIL;
}
static void locker_pthread_destroy(Locker* thiz)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
int ret = pthread_mutex_destroy(&priv->mutex);
SAFE_FREE(thiz);
return;
}
Locker* locker_pthread_create(void)
{
Locker* thiz = (Locker*)malloc(sizeof(Locker) + sizeof(PrivInfo));
if(thiz != NULL) {
PrivInfo* priv = (PrivInfo*)thiz->priv;
thiz->lock = locker_pthread_lock;
thiz->unlock = locker_pthread_unlock;
thiz->destroy = locker_pthread_destroy;
pthread_mutex_init(&(priv->mutex), NULL);
}
return thiz;
}
/*
* File: locker_nest.h
*/
#include "locker.h"
#ifndef LOCKER_NEST_H
#define LOCKER_NEST_H
DECLS_BEGIN
typedef int (*TaskSelfFunc)(void);
Locker* locker_nest_create(Locker* real_locker, TaskSelfFunc task_self);
DECLS_END
#endif/*LOCKER_NEST_H*/
/*
* File: locker_nest.c
*/
#include "locker_nest.h"
typedef struct _PrivInfo
{
int owner;
int refcount;
Locker* real_locker;
TaskSelfFunc task_self;
}PrivInfo;
static Ret locker_nest_lock(Locker* thiz)
{
Ret ret = RET_OK;
PrivInfo* priv = (PrivInfo*)thiz->priv;
if(priv->owner == priv->task_self()) {
priv->refcount++;
} else {
if( (ret = locker_lock(priv->real_locker)) == RET_OK) {
priv->refcount = 1;
priv->owner = priv->task_self();
}
}
return ret;
}
static Ret locker_nest_unlock(Locker* thiz)
{
Ret ret = RET_OK;
PrivInfo* priv = (PrivInfo*)thiz->priv;
return_val_if_fail(priv->owner == priv->task_self(), RET_FAIL);
priv->refcount--;
if(priv->refcount == 0) {
priv->owner = 0;
ret = locker_unlock(priv->real_locker);
}
return ret;
}
static void locker_nest_destroy(Locker* thiz)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
priv->owner = 0;
priv->refcount = 0;
locker_destroy(priv->real_locker);
priv->real_locker = NULL;
SAFE_FREE(thiz);
return;
}
Locker* locker_nest_create(Locker* real_locker, TaskSelfFunc task_self)
{
Locker* thiz = NULL;
return_val_if_fail(real_locker != NULL && task_self != NULL, NULL);
thiz = (Locker*)malloc(sizeof(Locker) + sizeof(PrivInfo));
if(thiz != NULL) {
PrivInfo* priv = (PrivInfo*)thiz->priv;
thiz->lock = locker_nest_lock;
thiz->unlock = locker_nest_unlock;
thiz->destroy = locker_nest_destroy;
priv->owner = 0;
priv->refcount = 0;
priv->real_locker = real_locker;
priv->task_self = task_self;
}
return thiz;
}
/*
* File: dlist.h
*/
#include <stdio.h>
#include "locker.h"
#ifndef DLIST_H
#define DLIST_H
DECLS_BEGIN
struct _DList;
typedef struct _DList DList;
typedef void (*DListDataDestroyFunc)(void* ctx, void* data);
typedef int (*DListDataCompareFunc)(void* ctx, void* data);
typedef Ret (*DListDataVisitFunc)(void* ctx, void* data);
DList* dlist_create(DListDataDestroyFunc data_destroy, void* ctx, Locker* locker);
Ret dlist_insert(DList* thiz, size_t index, void* data);
Ret dlist_prepend(DList* thiz, void* data);
Ret dlist_append(DList* thiz, void* data);
Ret dlist_delete(DList* thiz, size_t index);
Ret dlist_get_by_index(DList* thiz, size_t index, void** data);
Ret dlist_set_by_index(DList* thiz, size_t index, void* data);
size_t dlist_length(DList* thiz);
int dlist_find(DList* thiz, DListDataCompareFunc cmp, void* ctx);
Ret dlist_foreach(DList* thiz, DListDataVisitFunc visit, void* ctx);
void dlist_destroy(DList* thiz);
DECLS_END
#endif/*DLIST*/
/*
* File: dlist.c
*/
#include <stdlib.h>
#include <unistd.h>
#include "dlist.h"
typedef struct _DListNode
{
struct _DListNode* prev;
struct _DListNode* next;
void* data;
}DListNode;
struct _DList
{
Locker* locker;
DListNode* first;
void* data_destroy_ctx;
DListDataDestroyFunc data_destroy;
};
static void dlist_destroy_data(DList* thiz, void* data)
{
if(thiz->data_destroy != NULL){
thiz->data_destroy(thiz->data_destroy_ctx, data);
}
return;
}
static DListNode* dlist_create_node(DList* thiz, void* data)
{
DListNode* node = malloc(sizeof(DListNode));
if(node != NULL){
node->prev = NULL;
node->next = NULL;
node->data = data;
}
return node;
}
static void dlist_destroy_node(DList* thiz, DListNode* node)
{
if(node != NULL){
node->next = NULL;
node->prev = NULL;
dlist_destroy_data(thiz, node->data);
SAFE_FREE(node);
}
return;
}
static void dlist_lock(DList* thiz)
{
if(thiz->locker != NULL){
locker_lock(thiz->locker);
}
return;
}
static void dlist_unlock(DList* thiz)
{
if(thiz->locker != NULL) {
locker_unlock(thiz->locker);
}
return;
}
static void dlist_destroy_locker(DList* thiz)
{
if(thiz->locker != NULL) {
locker_unlock(thiz->locker);
locker_destroy(thiz->locker);
thiz->locker = NULL;
}
return;
}
DList* dlist_create(DListDataDestroyFunc data_destroy, void* ctx, Locker* locker)
{
DList* thiz = malloc(sizeof(DList));
if(thiz != NULL){
thiz->first = NULL;
thiz->locker = locker;
thiz->data_destroy = data_destroy;
thiz->data_destroy_ctx = ctx;
}
return thiz;
}
static DListNode* dlist_get_node(DList* thiz, size_t index, int fail_return_last)
{
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL, NULL);
iter = thiz->first;
while(iter != NULL && iter->next != NULL && index > 0) {
iter = iter->next;
index--;
}
if(!fail_return_last) {
iter = index > 0 ? NULL : iter;
}
return iter;
}
Ret dlist_insert(DList* thiz, size_t index, void* data)
{
Ret ret = RET_OK;
DListNode* node = NULL;
DListNode* cursor = NULL;
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
dlist_lock(thiz);
do
{
if((node = dlist_create_node(thiz, data)) == NULL) {
ret = RET_OOM;
break;
}
if(thiz->first == NULL) {
thiz->first = node;
break;
}
cursor = dlist_get_node(thiz, index, 1);
if(index < dlist_length(thiz)) {
node->next = cursor;
if(cursor->prev != NULL) {
cursor->prev->next = node;
}
cursor->prev = node;
if(thiz->first == cursor) {
thiz->first = node;
}
}else {
cursor->next = node;
node->prev = cursor;
}
}while(0);
dlist_unlock(thiz);
return ret;
}
Ret dlist_prepend(DList* thiz, void* data)
{
return dlist_insert(thiz, 0, data);
}
Ret dlist_append(DList* thiz, void* data)
{
return dlist_insert(thiz, -1, data);
}
Ret dlist_delete(DList* thiz, size_t index)
{
Ret ret = RET_OK;
DListNode* cursor = NULL;
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
dlist_lock(thiz);
cursor = dlist_get_node(thiz, index, 0);
do
{
if(cursor == NULL) {
ret = RET_INVALID_PARAMS;
break;
}
if(cursor != NULL) {
if(cursor == thiz->first) {
thiz->first = cursor->next;
}
if(cursor->next != NULL) {
cursor->next->prev = cursor->prev;
}
if(cursor->prev != NULL) {
cursor->prev->next = cursor->next;
}
dlist_destroy_node(thiz, cursor);
}
}while(0);
dlist_unlock(thiz);
return RET_OK;
}
Ret dlist_get_by_index(DList* thiz, size_t index, void** data)
{
DListNode* cursor = NULL;
return_val_if_fail(thiz != NULL && data != NULL, RET_INVALID_PARAMS);
dlist_lock(thiz);
cursor = dlist_get_node(thiz, index, 0);
if(cursor != NULL) {
*data = cursor->data;
}
dlist_unlock(thiz);
return cursor != NULL ? RET_OK : RET_INVALID_PARAMS;
}
Ret dlist_set_by_index(DList* thiz, size_t index, void* data)
{
DListNode* cursor = NULL;
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
dlist_lock(thiz);
cursor = dlist_get_node(thiz, index, 0);
if(cursor != NULL) {
cursor->data = data;
}
dlist_unlock(thiz);
return cursor != NULL ? RET_OK : RET_INVALID_PARAMS;
}
size_t dlist_length(DList* thiz)
{
size_t length = 0;
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL, 0);
dlist_lock(thiz);
iter = thiz->first;
while(iter != NULL) {
length++;
iter = iter->next;
}
dlist_unlock(thiz);
return length;
}
Ret dlist_foreach(DList* thiz, DListDataVisitFunc visit, void* ctx)
{
Ret ret = RET_OK;
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL && visit != NULL, RET_INVALID_PARAMS);
dlist_lock(thiz);
iter = thiz->first;
while(iter != NULL && ret != RET_STOP) {
ret = visit(ctx, iter->data);
iter = iter->next;
}
dlist_unlock(thiz);
return ret;
}
int dlist_find(DList* thiz, DListDataCompareFunc cmp, void* ctx)
{
int i = 0;
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL && cmp != NULL, -1);
dlist_lock(thiz);
iter = thiz->first;
while(iter != NULL) {
if(cmp(ctx, iter->data) == 0) {
break;
}
i++;
iter = iter->next;
}
dlist_unlock(thiz);
return i;
}
void dlist_destroy(DList* thiz)
{
DListNode* iter = NULL;
DListNode* next = NULL;
return_if_fail(thiz != NULL);
dlist_lock(thiz);
iter = thiz->first;
while(iter != NULL) {
next = iter->next;
dlist_destroy_node(thiz, iter);
iter = next;
}
thiz->first = NULL;
dlist_destroy_locker(thiz);
SAFE_FREE(thiz);
return;
}
#ifdef DLIST_TEST
#include <assert.h>
#include <pthread.h>
#include "locker_pthread.h"
#include "locker_nest.h"
static int cmp_int(void* ctx, void* data)
{
return (int)data - (int)ctx;
}
static Ret print_int(void* ctx, void* data)
{
printf("%d ", (int)data);
return RET_OK;
}
static Ret check_and_dec_int(void* ctx, void* data)
{
int* expected =(int*)ctx;
assert(*expected == (int)data);
(*expected)--;
return RET_OK;
}
static void test_int_dlist(void)
{
int i = 0;
int n = 100;
int data = 0;
DList* dlist = dlist_create(NULL, NULL, NULL);
for(i = 0; i < n; i++){
assert(dlist_append(dlist, (void*)i) == RET_OK);
assert(dlist_length(dlist) == (i + 1));
assert(dlist_get_by_index(dlist, i, (void**)&data) == RET_OK);
assert(data == i);
assert(dlist_set_by_index(dlist, i, (void*)(2*i)) == RET_OK);
assert(dlist_get_by_index(dlist, i, (void**)&data) == RET_OK);
assert(data == 2*i);
assert(dlist_set_by_index(dlist, i, (void*)i) == RET_OK);
assert(dlist_find(dlist, cmp_int, (void*)i) == i);
}
for(i = 0; i < n; i++){
assert(dlist_get_by_index(dlist, 0, (void**)&data) == RET_OK);
assert(data == (i));
assert(dlist_length(dlist) == (n-i));
assert(dlist_delete(dlist, 0) == RET_OK);
assert(dlist_length(dlist) == (n-i-1));
if((i + 1) < n){
assert(dlist_get_by_index(dlist, 0, (void**)&data) == RET_OK);
assert((int)data == (i+1));
}
}
assert(dlist_length(dlist) == 0);
for(i = 0; i < n; i++){
assert(dlist_prepend(dlist, (void*)i) == RET_OK);
assert(dlist_length(dlist) == (i + 1));
assert(dlist_get_by_index(dlist, 0, (void**)&data) == RET_OK);
assert(data == i);
assert(dlist_set_by_index(dlist, 0, (void*)(2*i)) == RET_OK);
assert(dlist_get_by_index(dlist, 0, (void**)&data) == RET_OK);
assert(data == 2*i);
assert(dlist_set_by_index(dlist, 0, (void*)i) == RET_OK);
}
i = n - 1;
assert(dlist_foreach(dlist, check_and_dec_int, &i) == RET_OK);
dlist_destroy(dlist);
return;
}
static void test_invalid_params(void)
{
printf("===========Warning is normal begin==============\n");
assert(dlist_length(NULL) == 0);
assert(dlist_prepend(NULL, 0) == RET_INVALID_PARAMS);
assert(dlist_append(NULL, 0) == RET_INVALID_PARAMS);
assert(dlist_delete(NULL, 0) == RET_INVALID_PARAMS);
assert(dlist_insert(NULL, 0, 0) == RET_INVALID_PARAMS);
assert(dlist_set_by_index(NULL, 0, 0) == RET_INVALID_PARAMS);
assert(dlist_get_by_index(NULL, 0, NULL) == RET_INVALID_PARAMS);
assert(dlist_find(NULL, NULL, NULL) < 0);
assert(dlist_foreach(NULL, NULL, NULL) == RET_INVALID_PARAMS);
printf("===========Warning is normal end==============\n");
return;
}
static void single_thread_test(void)
{
test_int_dlist();
test_invalid_params();
return;
}
static void* producer(void* param)
{
int i = 0;
DList* dlist = (DList*)param;
for(i = 0; i < 100; i++){
assert(dlist_append(dlist, (void*)i) == RET_OK);
}
sleep(1);
for(i = 0; i < 100; i++){
assert(dlist_prepend(dlist, (void*)i) == RET_OK);
}
return NULL;
}
static void* consumer(void* param)
{
int i = 0;
DList* dlist = (DList*)param;
for(i = 0; i < 200; i++){
usleep(20);
assert(dlist_delete(dlist, 0) == RET_OK);
}
return NULL;
}
static void multi_thread_test(void)
{
pthread_t consumer_tid = 0;
pthread_t producer_tid = 0;
Locker* locker = locker_pthread_create();
Locker* nest_locker = locker_nest_create(locker, (TaskSelfFunc)pthread_self);
DList* dlist = dlist_create(NULL, NULL, nest_locker);
pthread_create(&producer_tid, NULL, producer, dlist);
pthread_create(&consumer_tid, NULL, consumer, dlist);
pthread_join(consumer_tid, NULL);
pthread_join(producer_tid, NULL);
dlist_destroy(dlist);
return;
}
int main(int argc, char* argv[])
{
single_thread_test();
multi_thread_test();
return 0;
}
#endif
CFILES=dlist.c locker_pthread.c locker_nest.c
all:
gcc -g -m32 -shared -lpthread $(CFILES) -o libdlist.so
gcc -g -m32 -DDLIST_TEST $(CFILES) -o dlist_test -lpthread
clean:
rm -f *test *.exe *.so
4.4 读写锁¶
/*
* File: typedef.h
*/
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#ifndef TYPEDEF_H
#define TYPEDEF_H
typedef enum _Ret
{
RET_OK,
RET_OOM,
RET_STOP,
RET_INVALID_PARAMS,
RET_FAIL
}Ret;
#ifdef __cplusplus
#define DECLS_BEGIN extern "C" {
#define DECLS_END }
#else
#define DECLS_BEGIN
#define DECLS_END
#endif/*__cplusplus*/
#define return_if_fail(p) if(!(p)) \
{printf("%s:%d Warning: "#p" failed.\n", \
__func__, __LINE__); return;}
#define return_val_if_fail(p, ret) if(!(p)) \
{printf("%s:%d Warning: "#p" failed.\n",\
__func__, __LINE__); return (ret);}
#define SAFE_FREE(p) if(p != NULL) {free(p); p = NULL;}
#endif/*TYPEDEF_H*/
/*
* File: locker.h
*/
#include "typedef.h"
#ifndef LOCKER_H
#define LOCKER_H
DECLS_BEGIN
struct _Locker;
typedef struct _Locker Locker;
typedef Ret (*LockerLockFunc)(Locker* thiz);
typedef Ret (*LockerUnlockFunc)(Locker* thiz);
typedef void (*LockerDestroyFunc)(Locker* thiz);
struct _Locker
{
LockerLockFunc lock;
LockerUnlockFunc unlock;
LockerDestroyFunc destroy;
char priv[0];
};
static inline Ret locker_lock(Locker* thiz)
{
return_val_if_fail(thiz != NULL && thiz->lock != NULL, RET_INVALID_PARAMS);
return thiz->lock(thiz);
}
static inline Ret locker_unlock(Locker* thiz)
{
return_val_if_fail(thiz != NULL && thiz->unlock != NULL, RET_INVALID_PARAMS);
return thiz->unlock(thiz);
}
static inline void locker_destroy(Locker* thiz)
{
return_if_fail(thiz != NULL && thiz->destroy != NULL);
thiz->destroy(thiz);
return;
}
DECLS_END
#endif/*LOCKER_H*/
/*
* File: locker_pthread.h
*/
#include "locker.h"
#ifndef LOCKER_PTHREAD_H
#define LOCKER_PTHREAD_H
DECLS_BEGIN
Locker* locker_pthread_create(void);
DECLS_END
#endif/*LOCKER_PTHREAD_H*/
/*
* File: locker_pthread.c
*/
#include <stdlib.h>
#include <pthread.h>
#include "locker_pthread.h"
typedef struct _PrivInfo
{
pthread_mutex_t mutex;
}PrivInfo;
static Ret locker_pthread_lock(Locker* thiz)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
int ret = pthread_mutex_lock(&priv->mutex);
return ret == 0 ? RET_OK : RET_FAIL;
}
static Ret locker_pthread_unlock(Locker* thiz)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
int ret = pthread_mutex_unlock(&priv->mutex);
return ret == 0 ? RET_OK : RET_FAIL;
}
static void locker_pthread_destroy(Locker* thiz)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
int ret = pthread_mutex_destroy(&priv->mutex);
SAFE_FREE(thiz);
return;
}
Locker* locker_pthread_create(void)
{
Locker* thiz = (Locker*)malloc(sizeof(Locker) + sizeof(PrivInfo));
if(thiz != NULL)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
thiz->lock = locker_pthread_lock;
thiz->unlock = locker_pthread_unlock;
thiz->destroy = locker_pthread_destroy;
pthread_mutex_init(&(priv->mutex), NULL);
}
return thiz;
}
/*
* File: locker_nest.h
*/
#include "locker.h"
#ifndef LOCKER_NEST_H
#define LOCKER_NEST_H
DECLS_BEGIN
typedef int (*TaskSelfFunc)(void);
Locker* locker_nest_create(Locker* real_locker, TaskSelfFunc task_self);
DECLS_END
#endif/*LOCKER_NEST_H*/
/*
* File: locker_nest.c
*/
#include "locker_nest.h"
typedef struct _PrivInfo
{
int owner;
int refcount;
Locker* real_locker;
TaskSelfFunc task_self;
}PrivInfo;
static Ret locker_nest_lock(Locker* thiz)
{
Ret ret = RET_OK;
PrivInfo* priv = (PrivInfo*)thiz->priv;
if(priv->owner == priv->task_self())
{
priv->refcount++;
}
else
{
if( (ret = locker_lock(priv->real_locker)) == RET_OK)
{
priv->refcount = 1;
priv->owner = priv->task_self();
}
}
return ret;
}
static Ret locker_nest_unlock(Locker* thiz)
{
Ret ret = RET_OK;
PrivInfo* priv = (PrivInfo*)thiz->priv;
return_val_if_fail(priv->owner == priv->task_self(), RET_FAIL);
priv->refcount--;
if(priv->refcount == 0)
{
priv->owner = 0;
ret = locker_unlock(priv->real_locker);
}
return ret;
}
static void locker_nest_destroy(Locker* thiz)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
priv->owner = 0;
priv->refcount = 0;
locker_destroy(priv->real_locker);
priv->real_locker = NULL;
SAFE_FREE(thiz);
return;
}
Locker* locker_nest_create(Locker* real_locker, TaskSelfFunc task_self)
{
Locker* thiz = NULL;
return_val_if_fail(real_locker != NULL && task_self != NULL, NULL);
thiz = (Locker*)malloc(sizeof(Locker) + sizeof(PrivInfo));
if(thiz != NULL)
{
PrivInfo* priv = (PrivInfo*)thiz->priv;
thiz->lock = locker_nest_lock;
thiz->unlock = locker_nest_unlock;
thiz->destroy = locker_nest_destroy;
priv->owner = 0;
priv->refcount = 0;
priv->real_locker = real_locker;
priv->task_self = task_self;
}
return thiz;
}
/*
* File: rw_locker.h
*/
#include "locker.h"
#ifndef RW_LOCKR_H
#define RW_LOCKER_H
struct _RwLocker;
typedef struct _RwLocker RwLocker;
RwLocker* rw_locker_create(Locker* rw_locker, Locker* rd_locker);
Ret rw_locker_wrlock(RwLocker* thiz);
Ret rw_locker_rdlock(RwLocker* thiz);
Ret rw_locker_unlock(RwLocker* thiz);
void rw_locker_destroy(RwLocker* thiz);
#endif/*RW_LOCKER_H*/
/*
* File: rw_locker.c
*/
#include "rw_locker.h"
typedef enum _RwLockerMode
{
RW_LOCKER_NONE,
RW_LOCKER_WR,
RW_LOCKER_RD,
RW_LOCKER_NR
}RwLockerMode;
struct _RwLocker
{
int readers;
RwLockerMode mode;
Locker* rw_locker;
Locker* rd_locker;
};
RwLocker* rw_locker_create(Locker* rw_locker, Locker* rd_locker)
{
RwLocker* thiz = NULL;
return_val_if_fail(rw_locker != NULL && rd_locker != NULL, NULL);
thiz = (RwLocker*)malloc(sizeof(RwLocker));
if(thiz != NULL)
{
thiz->readers = 0;
thiz->mode = RW_LOCKER_NONE;
thiz->rw_locker = rw_locker;
thiz->rd_locker = rd_locker;
}
return thiz;
}
Ret rw_locker_wrlock(RwLocker* thiz)
{
Ret ret = RET_OK;
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
if((ret = locker_lock(thiz->rw_locker)) == RET_OK)
{
thiz->mode = RW_LOCKER_WR;
}
return ret;
}
Ret rw_locker_rdlock(RwLocker* thiz)
{
Ret ret = RET_OK;
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
if((ret = locker_lock(thiz->rd_locker)) == RET_OK)
{
thiz->readers++;
if(thiz->readers == 1)
{
ret = locker_lock(thiz->rw_locker);
thiz->mode = RW_LOCKER_RD;
}
locker_unlock(thiz->rd_locker);
}
return ret;
}
Ret rw_locker_unlock(RwLocker* thiz)
{
Ret ret = RET_OK;
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
if(thiz->mode == RW_LOCKER_WR)
{
thiz->mode == RW_LOCKER_NONE;
ret = locker_unlock(thiz->rw_locker);
}
else
{
assert(thiz->mode == RW_LOCKER_RD);
if((ret = locker_lock(thiz->rd_locker)) == RET_OK)
{
thiz->readers--;
if(thiz->readers == 0)
{
thiz->mode == RW_LOCKER_NONE;
ret = locker_unlock(thiz->rw_locker);
}
locker_unlock(thiz->rd_locker);
}
}
return ret;
}
void rw_locker_destroy(RwLocker* thiz)
{
if(thiz != NULL)
{
locker_destroy(thiz->rd_locker);
locker_destroy(thiz->rw_locker);
thiz->rd_locker = thiz->rw_locker = NULL;
SAFE_FREE(thiz);
}
return;
}
/*
* File: dlist.h
*/
#include <stdio.h>
#include "rw_locker.h"
#ifndef DLIST_H
#define DLIST_H
DECLS_BEGIN
struct _DList;
typedef struct _DList DList;
typedef void (*DListDataDestroyFunc)(void* ctx, void* data);
typedef int (*DListDataCompareFunc)(void* ctx, void* data);
typedef Ret (*DListDataVisitFunc)(void* ctx, void* data);
DList* dlist_create(DListDataDestroyFunc data_destroy, void* ctx, RwLocker* rw_locker);
Ret dlist_insert(DList* thiz, size_t index, void* data);
Ret dlist_prepend(DList* thiz, void* data);
Ret dlist_append(DList* thiz, void* data);
Ret dlist_delete(DList* thiz, size_t index);
Ret dlist_get_by_index(DList* thiz, size_t index, void** data);
Ret dlist_set_by_index(DList* thiz, size_t index, void* data);
size_t dlist_length(DList* thiz);
int dlist_find(DList* thiz, DListDataCompareFunc cmp, void* ctx);
Ret dlist_foreach(DList* thiz, DListDataVisitFunc visit, void* ctx);
void dlist_destroy(DList* thiz);
DECLS_END
#endif/*DLIST*/
/*
* File: dlist.c
*/
#include <stdlib.h>
#include <unistd.h>
#include "dlist.h"
typedef struct _DListNode
{
struct _DListNode* prev;
struct _DListNode* next;
void* data;
}DListNode;
struct _DList
{
RwLocker* rw_locker;
DListNode* first;
void* data_destroy_ctx;
DListDataDestroyFunc data_destroy;
};
size_t dlist_length_nolock(DList* thiz);
static void dlist_destroy_data(DList* thiz, void* data)
{
if(thiz->data_destroy != NULL)
{
thiz->data_destroy(thiz->data_destroy_ctx, data);
}
return;
}
static DListNode* dlist_create_node(DList* thiz, void* data)
{
DListNode* node = malloc(sizeof(DListNode));
if(node != NULL)
{
node->prev = NULL;
node->next = NULL;
node->data = data;
}
return node;
}
static void dlist_destroy_node(DList* thiz, DListNode* node)
{
if(node != NULL)
{
node->next = NULL;
node->prev = NULL;
dlist_destroy_data(thiz, node->data);
SAFE_FREE(node);
}
return;
}
static void dlist_wrlock(DList* thiz)
{
if(thiz->rw_locker != NULL)
{
rw_locker_wrlock(thiz->rw_locker);
}
return;
}
static void dlist_rdlock(DList* thiz)
{
if(thiz->rw_locker != NULL)
{
rw_locker_rdlock(thiz->rw_locker);
}
return;
}
static void dlist_unlock(DList* thiz)
{
if(thiz->rw_locker != NULL)
{
rw_locker_unlock(thiz->rw_locker);
}
return;
}
static void dlist_destroy_locker(DList* thiz)
{
if(thiz->rw_locker != NULL)
{
rw_locker_unlock(thiz->rw_locker);
rw_locker_destroy(thiz->rw_locker);
}
return;
}
DList* dlist_create(DListDataDestroyFunc data_destroy, void* ctx, RwLocker* rw_locker)
{
DList* thiz = malloc(sizeof(DList));
if(thiz != NULL)
{
thiz->first = NULL;
thiz->rw_locker = rw_locker;
thiz->data_destroy = data_destroy;
thiz->data_destroy_ctx = ctx;
}
return thiz;
}
static DListNode* dlist_get_node(DList* thiz, size_t index, int fail_return_last)
{
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL, NULL);
iter = thiz->first;
while(iter != NULL && iter->next != NULL && index > 0)
{
iter = iter->next;
index--;
}
if(!fail_return_last)
{
iter = index > 0 ? NULL : iter;
}
return iter;
}
Ret dlist_insert(DList* thiz, size_t index, void* data)
{
Ret ret = RET_OK;
DListNode* node = NULL;
DListNode* cursor = NULL;
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
dlist_wrlock(thiz);
do
{
if((node = dlist_create_node(thiz, data)) == NULL)
{
ret = RET_OOM;
break;
}
if(thiz->first == NULL)
{
thiz->first = node;
break;
}
cursor = dlist_get_node(thiz, index, 1);
if(index < dlist_length_nolock(thiz))
{
node->next = cursor;
if(cursor->prev != NULL)
{
cursor->prev->next = node;
}
cursor->prev = node;
if(thiz->first == cursor)
{
thiz->first = node;
}
}
else
{
cursor->next = node;
node->prev = cursor;
}
}while(0);
dlist_unlock(thiz);
return ret;
}
Ret dlist_prepend(DList* thiz, void* data)
{
return dlist_insert(thiz, 0, data);
}
Ret dlist_append(DList* thiz, void* data)
{
return dlist_insert(thiz, -1, data);
}
Ret dlist_delete(DList* thiz, size_t index)
{
Ret ret = RET_OK;
DListNode* cursor = NULL;
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
dlist_wrlock(thiz);
cursor = dlist_get_node(thiz, index, 0);
do
{
if(cursor == NULL)
{
ret = RET_INVALID_PARAMS;
break;
}
if(cursor != NULL)
{
if(cursor == thiz->first)
{
thiz->first = cursor->next;
}
if(cursor->next != NULL)
{
cursor->next->prev = cursor->prev;
}
if(cursor->prev != NULL)
{
cursor->prev->next = cursor->next;
}
dlist_destroy_node(thiz, cursor);
}
}while(0);
dlist_unlock(thiz);
return RET_OK;
}
Ret dlist_get_by_index(DList* thiz, size_t index, void** data)
{
DListNode* cursor = NULL;
return_val_if_fail(thiz != NULL && data != NULL, RET_INVALID_PARAMS);
dlist_rdlock(thiz);
cursor = dlist_get_node(thiz, index, 0);
if(cursor != NULL)
{
*data = cursor->data;
}
dlist_unlock(thiz);
return cursor != NULL ? RET_OK : RET_INVALID_PARAMS;
}
Ret dlist_set_by_index(DList* thiz, size_t index, void* data)
{
DListNode* cursor = NULL;
return_val_if_fail(thiz != NULL, RET_INVALID_PARAMS);
dlist_wrlock(thiz);
cursor = dlist_get_node(thiz, index, 0);
if(cursor != NULL)
{
cursor->data = data;
}
dlist_unlock(thiz);
return cursor != NULL ? RET_OK : RET_INVALID_PARAMS;
}
size_t dlist_length_nolock(DList* thiz)
{
size_t length = 0;
DListNode* iter = NULL;
iter = thiz->first;
while(iter != NULL)
{
length++;
iter = iter->next;
}
return length;
}
size_t dlist_length(DList* thiz)
{
size_t length = 0;
return_val_if_fail(thiz != NULL, 0);
dlist_rdlock(thiz);
length = dlist_length_nolock(thiz);
dlist_unlock(thiz);
return length;
}
Ret dlist_foreach(DList* thiz, DListDataVisitFunc visit, void* ctx)
{
Ret ret = RET_OK;
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL && visit != NULL, RET_INVALID_PARAMS);
dlist_rdlock(thiz);
iter = thiz->first;
while(iter != NULL && ret != RET_STOP)
{
ret = visit(ctx, iter->data);
iter = iter->next;
}
dlist_unlock(thiz);
return ret;
}
int dlist_find(DList* thiz, DListDataCompareFunc cmp, void* ctx)
{
int i = 0;
DListNode* iter = NULL;
return_val_if_fail(thiz != NULL && cmp != NULL, -1);
dlist_rdlock(thiz);
iter = thiz->first;
while(iter != NULL)
{
if(cmp(ctx, iter->data) == 0)
{
break;
}
i++;
iter = iter->next;
}
dlist_unlock(thiz);
return i;
}
void dlist_destroy(DList* thiz)
{
DListNode* iter = NULL;
DListNode* next = NULL;
return_if_fail(thiz != NULL);
dlist_wrlock(thiz);
iter = thiz->first;
while(iter != NULL)
{
next = iter->next;
dlist_destroy_node(thiz, iter);
iter = next;
}
thiz->first = NULL;
dlist_destroy_locker(thiz);
SAFE_FREE(thiz);
return;
}
#ifdef DLIST_TEST
#include <assert.h>
#include <pthread.h>
#include "locker_pthread.h"
#include "locker_nest.h"
static int cmp_int(void* ctx, void* data)
{
return (int)data - (int)ctx;
}
static Ret print_int(void* ctx, void* data)
{
printf("%d ", (int)data);
return RET_OK;
}
static Ret check_and_dec_int(void* ctx, void* data)
{
int* expected =(int*)ctx;
assert(*expected == (int)data);
(*expected)--;
return RET_OK;
}
static void test_int_dlist(void)
{
int i = 0;
int n = 100;
int data = 0;
DList* dlist = dlist_create(NULL, NULL, NULL);
for(i = 0; i < n; i++)
{
assert(dlist_append(dlist, (void*)i) == RET_OK);
assert(dlist_length(dlist) == (i + 1));
assert(dlist_get_by_index(dlist, i, (void**)&data) == RET_OK);
assert(data == i);
assert(dlist_set_by_index(dlist, i, (void*)(2*i)) == RET_OK);
assert(dlist_get_by_index(dlist, i, (void**)&data) == RET_OK);
assert(data == 2*i);
assert(dlist_set_by_index(dlist, i, (void*)i) == RET_OK);
assert(dlist_find(dlist, cmp_int, (void*)i) == i);
}
for(i = 0; i < n; i++)
{
assert(dlist_get_by_index(dlist, 0, (void**)&data) == RET_OK);
assert(data == (i));
assert(dlist_length(dlist) == (n-i));
assert(dlist_delete(dlist, 0) == RET_OK);
assert(dlist_length(dlist) == (n-i-1));
if((i + 1) < n)
{
assert(dlist_get_by_index(dlist, 0, (void**)&data) == RET_OK);
assert((int)data == (i+1));
}
}
assert(dlist_length(dlist) == 0);
for(i = 0; i < n; i++)
{
assert(dlist_prepend(dlist, (void*)i) == RET_OK);
assert(dlist_length(dlist) == (i + 1));
assert(dlist_get_by_index(dlist, 0, (void**)&data) == RET_OK);
assert(data == i);
assert(dlist_set_by_index(dlist, 0, (void*)(2*i)) == RET_OK);
assert(dlist_get_by_index(dlist, 0, (void**)&data) == RET_OK);
assert(data == 2*i);
assert(dlist_set_by_index(dlist, 0, (void*)i) == RET_OK);
}
i = n - 1;
assert(dlist_foreach(dlist, check_and_dec_int, &i) == RET_OK);
dlist_destroy(dlist);
return;
}
static void test_invalid_params(void)
{
printf("===========Warning is normal begin==============\n");
assert(dlist_length(NULL) == 0);
assert(dlist_prepend(NULL, 0) == RET_INVALID_PARAMS);
assert(dlist_append(NULL, 0) == RET_INVALID_PARAMS);
assert(dlist_delete(NULL, 0) == RET_INVALID_PARAMS);
assert(dlist_insert(NULL, 0, 0) == RET_INVALID_PARAMS);
assert(dlist_set_by_index(NULL, 0, 0) == RET_INVALID_PARAMS);
assert(dlist_get_by_index(NULL, 0, NULL) == RET_INVALID_PARAMS);
assert(dlist_find(NULL, NULL, NULL) < 0);
assert(dlist_foreach(NULL, NULL, NULL) == RET_INVALID_PARAMS);
printf("===========Warning is normal end==============\n");
return;
}
static void single_thread_test(void)
{
test_int_dlist();
test_invalid_params();
return;
}
#define NR 1000
static void* producer(void* param)
{
int i = 0;
DList* dlist = (DList*)param;
for(i = 0; i < NR; i++)
{
assert(dlist_append(dlist, (void*)i) == RET_OK);
}
sleep(1);
for(i = 0; i < NR; i++)
{
assert(dlist_prepend(dlist, (void*)i) == RET_OK);
}
for(i = 0; i < NR; i++)
{
assert(dlist_insert(dlist, i, (void*)i) == RET_OK);
}
return NULL;
}
static void* consumer(void* param)
{
int i = 0;
DList* dlist = (DList*)param;
for(i = 0; i < 3 * NR; i++)
{
usleep(20);
assert(dlist_delete(dlist, 0) == RET_OK);
}
return NULL;
}
static void* reader(void* param)
{
int i = 0;
DList* dlist = (DList*)param;
for(i = 0; i < NR; i++)
{
int length = dlist_length(dlist);
dlist_find(dlist, cmp_int, (void*)i);
}
return NULL;
}
static void multi_thread_test(void)
{
pthread_t consumer_tid = 0;
pthread_t producer_tid = 0;
pthread_t reader_tid = 0;
Locker* rw_locker = locker_pthread_create();
Locker* rd_locker = locker_pthread_create();
DList* dlist = dlist_create(NULL, NULL,
rw_locker_create(rw_locker, rd_locker));
pthread_create(&producer_tid, NULL, producer, dlist);
pthread_create(&consumer_tid, NULL, consumer, dlist);
pthread_create(&reader_tid, NULL, reader, dlist);
pthread_join(consumer_tid, NULL);
pthread_join(producer_tid, NULL);
pthread_join(reader_tid, NULL);
printf("length=%d\n", dlist_length(dlist));
dlist_destroy(dlist);
return;
}
int main(int argc, char* argv[])
{
single_thread_test();
multi_thread_test();
return 0;
}
#endif
CFILES=dlist.c locker_pthread.c locker_nest.c rw_locker.c
all:
gcc -g -m32 -shared -lpthread $(CFILES) -o libdlist.so
gcc -g -m32 -DDLIST_TEST $(CFILES) -o dlist_test -lpthread
clean:
rm -f *test *.exe *.so
4.5 无锁数据结构¶
/*
* File: typedef.h
*/
#include <stdio.h>
#include <assert.h>
#include <stdlib.h>
#ifndef TYPEDEF_H
#define TYPEDEF_H
typedef enum _Ret
{
RET_OK,
RET_OOM,
RET_STOP,
RET_INVALID_PARAMS,
RET_FAIL
}Ret;
#ifdef __cplusplus
#define DECLS_BEGIN extern "C" {
#define DECLS_END }
#else
#define DECLS_BEGIN
#define DECLS_END
#endif/*__cplusplus*/
#define return_if_fail(p) if(!(p)) \
{printf("%s:%d Warning: "#p" failed.\n", \
__func__, __LINE__); return;}
#define return_val_if_fail(p, ret) if(!(p)) \
{printf("%s:%d Warning: "#p" failed.\n",\
__func__, __LINE__); return (ret);}
#define SAFE_FREE(p) if(p != NULL) {free(p); p = NULL;}
#endif/*TYPEDEF_H*/
/*
* File: fifo_ring.c
*/
#include "typedef.h"
typedef struct _FifoRing
{
int r_cursor;
int w_cursor;
size_t length;
void* data[0];
}FifoRing;
FifoRing* fifo_ring_create(size_t length)
{
FifoRing* thiz = NULL;
return_val_if_fail(length > 1, NULL);
thiz = (FifoRing*)malloc(sizeof(FifoRing) + length * sizeof(void*));
if(thiz != NULL) {
thiz->r_cursor = 0;
thiz->w_cursor = 0;
thiz->length = length;
}
return thiz;
}
Ret fifo_ring_pop(FifoRing* thiz, void** data)
{
Ret ret = RET_FAIL;
return_val_if_fail(thiz != NULL && data != NULL, RET_FAIL);
if(thiz->r_cursor != thiz->w_cursor) {
*data = thiz->data[thiz->r_cursor];
thiz->r_cursor = (thiz->r_cursor + 1)%thiz->length;
ret = RET_OK;
}
return ret;
}
Ret fifo_ring_push(FifoRing* thiz, void* data)
{
int w_cursor = 0;
Ret ret = RET_FAIL;
return_val_if_fail(thiz != NULL, RET_FAIL);
w_cursor = (thiz->w_cursor + 1) % thiz->length;
if(w_cursor != thiz->r_cursor) {
thiz->data[thiz->w_cursor] = data;
thiz->w_cursor = w_cursor;
ret = RET_OK;
}
return ret;
}
void fifo_ring_destroy(FifoRing* thiz)
{
if(thiz != NULL) {
free(thiz);
}
return;
}
#ifdef FIFO_RING_TEST
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
//#define NR 1000000
#define NR 1000
static void* thread_push(void* param)
{
int i = 0;
FifoRing* fifo = (FifoRing*)param;
while(i < NR) {
if(fifo_ring_push(fifo, (void*)i) == RET_OK) {
i++;
}else{
usleep(10);
}
}
return NULL;
}
static void* thread_pop(void* param)
{
int i = 0;
void* data = 0;
FifoRing* fifo = (FifoRing*)param;
while(i < NR) {
if(fifo_ring_pop(fifo, (void**)&data) == RET_OK) {
assert(i == (int)data);
i++;
}else{
usleep(10);
}
}
return NULL;
}
void push_pop_test(size_t fifo_length)
{
pthread_t push_tid = 0;
pthread_t pop_tid = 0;
FifoRing* fifo = fifo_ring_create(fifo_length);
pthread_create(&push_tid, NULL, thread_push, fifo);
pthread_create(&pop_tid, NULL, thread_pop, fifo);
pthread_join(push_tid, NULL);
pthread_join(pop_tid, NULL);
fifo_ring_destroy(fifo);
return;
}
int main(int argc, char* argv[])
{
push_pop_test(2);
push_pop_test(20);
push_pop_test(200);
push_pop_test(2000);
return 0;
}
#endif/*FIFO_RING_TEST*/
/*
* File: dlist.h
*/
#include <stdio.h>
#include "locker.h"
#ifndef DLIST_H
#define DLIST_H
DECLS_BEGIN
struct _DList;
typedef struct _DList DList;
typedef void (*DListDataDestroyFunc)(void* ctx, void* data);
typedef int (*DListDataCompareFunc)(void* ctx, void* data);
typedef Ret (*DListDataVisitFunc)(void* ctx, void* data);
DList* dlist_create(DListDataDestroyFunc data_destroy, void* ctx, Locker* locker);
Ret dlist_insert(DList* thiz, size_t index, void* data);
Ret dlist_prepend(DList* thiz, void* data);
Ret dlist_append(DList* thiz, void* data);
Ret dlist_delete(DList* thiz, size_t index);
Ret dlist_get_by_index(DList* thiz, size_t index, void** data);
Ret dlist_set_by_index(DList* thiz, size_t index, void* data);
size_t dlist_length(DList* thiz);
int dlist_find(DList* thiz, DListDataCompareFunc cmp, void* ctx);
Ret dlist_foreach(DList* thiz, DListDataVisitFunc visit, void* ctx);
void dlist_destroy(DList* thiz);
DECLS_END
#endif/*DLIST*/
/*
* File: swmr_dlist.c
*/
#include <unistd.h>
#include "dlist.h"
#include "iatomic.h"
#define CAS(_a, _o, _n) \
({ __typeof__(_o) __o = _o; \
__asm__ __volatile__( \
"lock cmpxchg %3,%1" \
: "=a" (__o), "=m" (*(volatile unsigned int *)(_a)) \
: "0" (__o), "r" (_n) ); \
__o; \
})
typedef struct _SwmrDList
{
atomic_t rd_index_and_ref;
DList* dlists[2];
}SwmrDList;
void swmr_dlist_destroy(SwmrDList* thiz);
SwmrDList* swmr_dlist_create(DListDataDestroyFunc data_destroy, void* ctx)
{
Ret ret = RET_FAIL;
SwmrDList* thiz = (SwmrDList*)calloc(1, sizeof(SwmrDList));
do
{
if((thiz->dlists[0] = dlist_create(data_destroy, ctx, NULL)) == NULL) {
break;
}
if((thiz->dlists[1] = dlist_create(data_destroy, ctx, NULL)) == NULL) {
break;
}
ret = RET_OK;
}while(0);
if(ret != RET_OK) {
swmr_dlist_destroy(thiz);
thiz = NULL;
}
return thiz;
}
void swmr_dlist_destroy(SwmrDList* thiz)
{
if(thiz != NULL) {
if(thiz->dlists != NULL) {
dlist_destroy(thiz->dlists[0]);
dlist_destroy(thiz->dlists[1]);
thiz->dlists[0] = NULL;
thiz->dlists[1] = NULL;
}
free(thiz);
}
return;
}
int swmr_dlist_find(SwmrDList* thiz, DListDataCompareFunc cmp, void* ctx)
{
int ret = 0;
return_val_if_fail(thiz != NULL && thiz->dlists != NULL, -1);
atomic_inc(&(thiz->rd_index_and_ref));
size_t rd_index = (thiz->rd_index_and_ref.counter>>24) & 0x1;
ret = dlist_find(thiz->dlists[rd_index], cmp, ctx);
atomic_dec(&(thiz->rd_index_and_ref));
return ret;
}
int swmr_dlist_length(SwmrDList* thiz)
{
int ret = 0;
return_val_if_fail(thiz != NULL && thiz->dlists != NULL, -1);
atomic_inc(&(thiz->rd_index_and_ref));
size_t rd_index = (thiz->rd_index_and_ref.counter>>24) & 0x1;
ret = dlist_length(thiz->dlists[rd_index]);
atomic_dec(&(thiz->rd_index_and_ref));
return ret;
}
Ret swmr_dlist_insert(SwmrDList* thiz, size_t index, void* data)
{
Ret ret = RET_FAIL;
DList* wr_dlist = NULL;
return_val_if_fail(thiz != NULL && thiz->dlists != NULL, ret);
size_t wr_index = !((thiz->rd_index_and_ref.counter>>24) & 0x1);
if((ret = dlist_insert(thiz->dlists[wr_index], index, data)) == RET_OK) {
int rd_index_old = thiz->rd_index_and_ref.counter & 0xFF000000;
int rd_index_new = wr_index << 24;
do
{
usleep(100);
}while(CAS(&(thiz->rd_index_and_ref), rd_index_old, rd_index_new));
wr_index = rd_index_old>>24;
ret = dlist_insert(thiz->dlists[wr_index], index, data);
}
return ret;
}
Ret swmr_dlist_delete(SwmrDList* thiz, size_t index)
{
Ret ret = RET_FAIL;
DList* wr_dlist = NULL;
return_val_if_fail(thiz != NULL && thiz->dlists != NULL, ret);
size_t wr_index = !((thiz->rd_index_and_ref.counter>>24) & 0x1);
if((ret = dlist_delete(thiz->dlists[wr_index], index)) == RET_OK) {
int rd_index_old = thiz->rd_index_and_ref.counter & 0xFF000000;
int rd_index_new = wr_index << 24;
do
{
usleep(100);
}while(CAS(&(thiz->rd_index_and_ref), rd_index_old, rd_index_new));
wr_index = rd_index_old>>24;
ret = dlist_delete(thiz->dlists[wr_index], index);
}
return ret;
}
#ifdef SWMR_DLIST_TEST
#include <pthread.h>
#define NR 1000
static int cmp_int(void* ctx, void* data)
{
return (int)data - (int)ctx;
}
static void* reader(void* param)
{
int i = 0;
int j = 0;
size_t length = 0;
SwmrDList* swmr = (SwmrDList*)param;
for(j = 0; j < NR; j++) {
for(i = 0; i < swmr_dlist_length(swmr); i++) {
assert(swmr_dlist_find(swmr, cmp_int, (void*)i) == i);
}
}
return NULL;
}
static void* writer(void* param)
{
int i = 0;
SwmrDList* swmr = (SwmrDList*)param;
for(i = 0; i < NR; i++) {
swmr_dlist_insert(swmr, i, (void*)i);
}
for(; i>0; i--) {
swmr_dlist_delete(swmr, i);
}
return NULL;
}
#define RD_NR 100
int main(int argc, char* argv[])
{
int i = 0;
pthread_t wr_tid = 0;
pthread_t rd_tids[RD_NR] = {0};
SwmrDList* swmr = swmr_dlist_create(NULL, NULL);
pthread_create(&wr_tid, NULL, writer, swmr);
for(i = 0; i < RD_NR; i++) {
pthread_create(rd_tids+i, NULL, reader, swmr);
}
for(i = 0; i < RD_NR; i++) {
pthread_join(rd_tids[i], NULL);
}
pthread_join(wr_tid, NULL);
return 0;
}
#endif/*SWMR_DLIST_TEST*/
#ifndef __ALSA_IATOMIC_H
#define __ALSA_IATOMIC_H
#if defined(__i386__) || defined(__x86_64__)
/*
* Atomic operations that C can't guarantee us. Useful for
* resource counting etc..
*/
#define ATOMIC_SMP_LOCK "lock ; "
/*
* Make sure gcc doesn't try to be clever and move things around
* on us. We need to use _exactly_ the address the user gave us,
* not some alias that contains the same information.
*/
typedef struct { volatile int counter; } atomic_t;
#define ATOMIC_INIT(i) { (i) }
/**
* atomic_read - read atomic variable
* @v: pointer of type atomic_t
*
* Atomically reads the value of @v. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
#define atomic_read(v) ((v)->counter)
/**
* atomic_set - set atomic variable
* @v: pointer of type atomic_t
* @i: required value
*
* Atomically sets the value of @v to @i. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
#define atomic_set(v,i) (((v)->counter) = (i))
/**
* atomic_add - add integer to atomic variable
* @i: integer value to add
* @v: pointer of type atomic_t
*
* Atomically adds @i to @v. Note that the guaranteed useful range
* of an atomic_t is only 24 bits.
*/
static __inline__ void atomic_add(int i, atomic_t *v)
{
__asm__ __volatile__(
ATOMIC_SMP_LOCK "addl %1,%0"
:"=m" (v->counter)
:"ir" (i), "m" (v->counter));
}
/**
* atomic_sub - subtract the atomic variable
* @i: integer value to subtract
* @v: pointer of type atomic_t
*
* Atomically subtracts @i from @v. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
static __inline__ void atomic_sub(int i, atomic_t *v)
{
__asm__ __volatile__(
ATOMIC_SMP_LOCK "subl %1,%0"
:"=m" (v->counter)
:"ir" (i), "m" (v->counter));
}
/**
* atomic_sub_and_test - subtract value from variable and test result
* @i: integer value to subtract
* @v: pointer of type atomic_t
*
* Atomically subtracts @i from @v and returns
* true if the result is zero, or false for all
* other cases. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
static __inline__ int atomic_sub_and_test(int i, atomic_t *v)
{
unsigned char c;
__asm__ __volatile__(
ATOMIC_SMP_LOCK "subl %2,%0; sete %1"
:"=m" (v->counter), "=qm" (c)
:"ir" (i), "m" (v->counter) : "memory");
return c;
}
/**
* atomic_inc - increment atomic variable
* @v: pointer of type atomic_t
*
* Atomically increments @v by 1. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
static __inline__ void atomic_inc(atomic_t *v)
{
__asm__ __volatile__(
ATOMIC_SMP_LOCK "incl %0"
:"=m" (v->counter)
:"m" (v->counter));
}
/**
* atomic_dec - decrement atomic variable
* @v: pointer of type atomic_t
*
* Atomically decrements @v by 1. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
static __inline__ void atomic_dec(atomic_t *v)
{
__asm__ __volatile__(
ATOMIC_SMP_LOCK "decl %0"
:"=m" (v->counter)
:"m" (v->counter));
}
/**
* atomic_dec_and_test - decrement and test
* @v: pointer of type atomic_t
*
* Atomically decrements @v by 1 and
* returns true if the result is 0, or false for all other
* cases. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
static __inline__ int atomic_dec_and_test(atomic_t *v)
{
unsigned char c;
__asm__ __volatile__(
ATOMIC_SMP_LOCK "decl %0; sete %1"
:"=m" (v->counter), "=qm" (c)
:"m" (v->counter) : "memory");
return c != 0;
}
/**
* atomic_inc_and_test - increment and test
* @v: pointer of type atomic_t
*
* Atomically increments @v by 1
* and returns true if the result is zero, or false for all
* other cases. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
static __inline__ int atomic_inc_and_test(atomic_t *v)
{
unsigned char c;
__asm__ __volatile__(
ATOMIC_SMP_LOCK "incl %0; sete %1"
:"=m" (v->counter), "=qm" (c)
:"m" (v->counter) : "memory");
return c != 0;
}
/**
* atomic_add_negative - add and test if negative
* @v: pointer of type atomic_t
* @i: integer value to add
*
* Atomically adds @i to @v and returns true
* if the result is negative, or false when
* result is greater than or equal to zero. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
static __inline__ int atomic_add_negative(int i, atomic_t *v)
{
unsigned char c;
__asm__ __volatile__(
ATOMIC_SMP_LOCK "addl %2,%0; sets %1"
:"=m" (v->counter), "=qm" (c)
:"ir" (i), "m" (v->counter) : "memory");
return c;
}
/* These are x86-specific, used by some header files */
#define atomic_clear_mask(mask, addr) \
__asm__ __volatile__(ATOMIC_SMP_LOCK "andl %0,%1" \
: : "r" (~(mask)),"m" (*addr) : "memory")
#define atomic_set_mask(mask, addr) \
__asm__ __volatile__(ATOMIC_SMP_LOCK "orl %0,%1" \
: : "r" (mask),"m" (*addr) : "memory")
/*
* Force strict CPU ordering.
* And yes, this is required on UP too when we're talking
* to devices.
*
* For now, "wmb()" doesn't actually do anything, as all
* Intel CPU's follow what Intel calls a *Processor Order*,
* in which all writes are seen in the program order even
* outside the CPU.
*
* I expect future Intel CPU's to have a weaker ordering,
* but I'd also expect them to finally get their act together
* and add some real memory barriers if so.
*/
#ifdef __i386__
#define mb() __asm__ __volatile__ ("lock; addl $0,0(%%esp)": : :"memory")
#define rmb() mb()
#define wmb() __asm__ __volatile__ ("": : :"memory")
#else
#define mb() asm volatile("mfence":::"memory")
#define rmb() asm volatile("lfence":::"memory")
#define wmb() asm volatile("sfence":::"memory")
#endif
#undef ATOMIC_SMP_LOCK
#define IATOMIC_DEFINED 1
#endif /* __i386__ */
#ifdef __ia64__
/*
* On IA-64, counter must always be volatile to ensure that that the
* memory accesses are ordered.
*/
typedef struct { volatile int counter; } atomic_t;
#define ATOMIC_INIT(i) ((atomic_t) { (i) })
#define atomic_read(v) ((v)->counter)
#define atomic_set(v,i) (((v)->counter) = (i))
/* stripped version - we need only 4byte version */
#define ia64_cmpxchg(sem,ptr,old,new,size) \
({ \
__typeof__(ptr) _p_ = (ptr); \
__typeof__(new) _n_ = (new); \
unsigned long _o_, _r_; \
_o_ = (unsigned int) (long) (old); \
__asm__ __volatile__ ("mov ar.ccv=%0;;" :: "rO"(_o_)); \
__asm__ __volatile__ ("cmpxchg4."sem" %0=[%1],%2,ar.ccv" \
: "=r"(_r_) : "r"(_p_), "r"(_n_) : "memory"); \
(__typeof__(old)) _r_; \
})
static __inline__ int
ia64_atomic_add (int i, atomic_t *v)
{
int old, new;
// CMPXCHG_BUGCHECK_DECL
do {
// CMPXCHG_BUGCHECK(v);
old = atomic_read(v);
new = old + i;
} while (ia64_cmpxchg("acq", v, old, old + i, sizeof(atomic_t)) != old);
return new;
}
static __inline__ int
ia64_atomic_sub (int i, atomic_t *v)
{
int old, new;
// CMPXCHG_BUGCHECK_DECL
do {
// CMPXCHG_BUGCHECK(v);
old = atomic_read(v);
new = old - i;
} while (ia64_cmpxchg("acq", v, old, new, sizeof(atomic_t)) != old);
return new;
}
#define IA64_FETCHADD(tmp,v,n,sz) \
({ \
switch (sz) { \
case 4: \
__asm__ __volatile__ ("fetchadd4.rel %0=[%1],%2" \
: "=r"(tmp) : "r"(v), "i"(n) : "memory"); \
break; \
\
case 8: \
__asm__ __volatile__ ("fetchadd8.rel %0=[%1],%2" \
: "=r"(tmp) : "r"(v), "i"(n) : "memory"); \
break; \
} \
})
#define ia64_fetch_and_add(i,v) \
({ \
unsigned long _tmp; \
volatile __typeof__(*(v)) *_v = (v); \
switch (i) { \
case -16: IA64_FETCHADD(_tmp, _v, -16, sizeof(*(v))); break; \
case -8: IA64_FETCHADD(_tmp, _v, -8, sizeof(*(v))); break; \
case -4: IA64_FETCHADD(_tmp, _v, -4, sizeof(*(v))); break; \
case -1: IA64_FETCHADD(_tmp, _v, -1, sizeof(*(v))); break; \
case 1: IA64_FETCHADD(_tmp, _v, 1, sizeof(*(v))); break; \
case 4: IA64_FETCHADD(_tmp, _v, 4, sizeof(*(v))); break; \
case 8: IA64_FETCHADD(_tmp, _v, 8, sizeof(*(v))); break; \
case 16: IA64_FETCHADD(_tmp, _v, 16, sizeof(*(v))); break; \
} \
(__typeof__(*v)) (_tmp + (i)); /* return new value */ \
})
/*
* Atomically add I to V and return TRUE if the resulting value is
* negative.
*/
static __inline__ int
atomic_add_negative (int i, atomic_t *v)
{
return ia64_atomic_add(i, v) < 0;
}
#define atomic_add_return(i,v) \
((__builtin_constant_p(i) && \
( (i == 1) || (i == 4) || (i == 8) || (i == 16) \
|| (i == -1) || (i == -4) || (i == -8) || (i == -16))) \
? ia64_fetch_and_add(i, &(v)->counter) \
: ia64_atomic_add(i, v))
#define atomic_sub_return(i,v) \
((__builtin_constant_p(i) && \
( (i == 1) || (i == 4) || (i == 8) || (i == 16) \
|| (i == -1) || (i == -4) || (i == -8) || (i == -16))) \
? ia64_fetch_and_add(-(i), &(v)->counter) \
: ia64_atomic_sub(i, v))
#define atomic_dec_return(v) atomic_sub_return(1, (v))
#define atomic_inc_return(v) atomic_add_return(1, (v))
#define atomic_sub_and_test(i,v) (atomic_sub_return((i), (v)) == 0)
#define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0)
#define atomic_inc_and_test(v) (atomic_add_return(1, (v)) != 0)
#define atomic_add(i,v) atomic_add_return((i), (v))
#define atomic_sub(i,v) atomic_sub_return((i), (v))
#define atomic_inc(v) atomic_add(1, (v))
#define atomic_dec(v) atomic_sub(1, (v))
/*
* Macros to force memory ordering. In these descriptions, "previous"
* and "subsequent" refer to program order; "visible" means that all
* architecturally visible effects of a memory access have occurred
* (at a minimum, this means the memory has been read or written).
*
* wmb(): Guarantees that all preceding stores to memory-
* like regions are visible before any subsequent
* stores and that all following stores will be
* visible only after all previous stores.
* rmb(): Like wmb(), but for reads.
* mb(): wmb()/rmb() combo, i.e., all previous memory
* accesses are visible before all subsequent
* accesses and vice versa. This is also known as
* a "fence."
*
* Note: "mb()" and its variants cannot be used as a fence to order
* accesses to memory mapped I/O registers. For that, mf.a needs to
* be used. However, we don't want to always use mf.a because (a)
* it's (presumably) much slower than mf and (b) mf.a is supported for
* sequential memory pages only.
*/
#define mb() __asm__ __volatile__ ("mf" ::: "memory")
#define rmb() mb()
#define wmb() mb()
#define IATOMIC_DEFINED 1
#endif /* __ia64__ */
#ifdef __alpha__
/*
* Atomic operations that C can't guarantee us. Useful for
* resource counting etc...
*
* But use these as seldom as possible since they are much slower
* than regular operations.
*/
/*
* Counter is volatile to make sure gcc doesn't try to be clever
* and move things around on us. We need to use _exactly_ the address
* the user gave us, not some alias that contains the same information.
*/
typedef struct { volatile int counter; } atomic_t;
#define ATOMIC_INIT(i) ( (atomic_t) { (i) } )
#define atomic_read(v) ((v)->counter)
#define atomic_set(v,i) ((v)->counter = (i))
/*
* To get proper branch prediction for the main line, we must branch
* forward to code at the end of this object's .text section, then
* branch back to restart the operation.
*/
static __inline__ void atomic_add(int i, atomic_t * v)
{
unsigned long temp;
__asm__ __volatile__(
"1: ldl_l %0,%1\n"
" addl %0,%2,%0\n"
" stl_c %0,%1\n"
" beq %0,2f\n"
".subsection 2\n"
"2: br 1b\n"
".previous"
:"=&r" (temp), "=m" (v->counter)
:"Ir" (i), "m" (v->counter));
}
static __inline__ void atomic_sub(int i, atomic_t * v)
{
unsigned long temp;
__asm__ __volatile__(
"1: ldl_l %0,%1\n"
" subl %0,%2,%0\n"
" stl_c %0,%1\n"
" beq %0,2f\n"
".subsection 2\n"
"2: br 1b\n"
".previous"
:"=&r" (temp), "=m" (v->counter)
:"Ir" (i), "m" (v->counter));
}
/*
* Same as above, but return the result value
*/
static __inline__ long atomic_add_return(int i, atomic_t * v)
{
long temp, result;
__asm__ __volatile__(
"1: ldl_l %0,%1\n"
" addl %0,%3,%2\n"
" addl %0,%3,%0\n"
" stl_c %0,%1\n"
" beq %0,2f\n"
" mb\n"
".subsection 2\n"
"2: br 1b\n"
".previous"
:"=&r" (temp), "=m" (v->counter), "=&r" (result)
:"Ir" (i), "m" (v->counter) : "memory");
return result;
}
static __inline__ long atomic_sub_return(int i, atomic_t * v)
{
long temp, result;
__asm__ __volatile__(
"1: ldl_l %0,%1\n"
" subl %0,%3,%2\n"
" subl %0,%3,%0\n"
" stl_c %0,%1\n"
" beq %0,2f\n"
" mb\n"
".subsection 2\n"
"2: br 1b\n"
".previous"
:"=&r" (temp), "=m" (v->counter), "=&r" (result)
:"Ir" (i), "m" (v->counter) : "memory");
return result;
}
#define atomic_dec_return(v) atomic_sub_return(1,(v))
#define atomic_inc_return(v) atomic_add_return(1,(v))
#define atomic_sub_and_test(i,v) (atomic_sub_return((i), (v)) == 0)
#define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0)
#define atomic_inc(v) atomic_add(1,(v))
#define atomic_dec(v) atomic_sub(1,(v))
#define mb() \
__asm__ __volatile__("mb": : :"memory")
#define rmb() \
__asm__ __volatile__("mb": : :"memory")
#define wmb() \
__asm__ __volatile__("wmb": : :"memory")
#define IATOMIC_DEFINED 1
#endif /* __alpha__ */
#ifdef __powerpc__
typedef struct { volatile int counter; } atomic_t;
#define ATOMIC_INIT(i) { (i) }
#define atomic_read(v) ((v)->counter)
#define atomic_set(v,i) (((v)->counter) = (i))
extern void atomic_clear_mask(unsigned long mask, unsigned long *addr);
extern void atomic_set_mask(unsigned long mask, unsigned long *addr);
#define SMP_ISYNC "\n\tisync"
static __inline__ void atomic_add(int a, atomic_t *v)
{
int t;
__asm__ __volatile__(
"1: lwarx %0,0,%3 # atomic_add\n\
add %0,%2,%0\n\
stwcx. %0,0,%3\n\
bne- 1b"
: "=&r" (t), "=m" (v->counter)
: "r" (a), "r" (&v->counter), "m" (v->counter)
: "cc");
}
static __inline__ int atomic_add_return(int a, atomic_t *v)
{
int t;
__asm__ __volatile__(
"1: lwarx %0,0,%2 # atomic_add_return\n\
add %0,%1,%0\n\
stwcx. %0,0,%2\n\
bne- 1b"
SMP_ISYNC
: "=&r" (t)
: "r" (a), "r" (&v->counter)
: "cc", "memory");
return t;
}
static __inline__ void atomic_sub(int a, atomic_t *v)
{
int t;
__asm__ __volatile__(
"1: lwarx %0,0,%3 # atomic_sub\n\
subf %0,%2,%0\n\
stwcx. %0,0,%3\n\
bne- 1b"
: "=&r" (t), "=m" (v->counter)
: "r" (a), "r" (&v->counter), "m" (v->counter)
: "cc");
}
static __inline__ int atomic_sub_return(int a, atomic_t *v)
{
int t;
__asm__ __volatile__(
"1: lwarx %0,0,%2 # atomic_sub_return\n\
subf %0,%1,%0\n\
stwcx. %0,0,%2\n\
bne- 1b"
SMP_ISYNC
: "=&r" (t)
: "r" (a), "r" (&v->counter)
: "cc", "memory");
return t;
}
static __inline__ void atomic_inc(atomic_t *v)
{
int t;
__asm__ __volatile__(
"1: lwarx %0,0,%2 # atomic_inc\n\
addic %0,%0,1\n\
stwcx. %0,0,%2\n\
bne- 1b"
: "=&r" (t), "=m" (v->counter)
: "r" (&v->counter), "m" (v->counter)
: "cc");
}
static __inline__ int atomic_inc_return(atomic_t *v)
{
int t;
__asm__ __volatile__(
"1: lwarx %0,0,%1 # atomic_inc_return\n\
addic %0,%0,1\n\
stwcx. %0,0,%1\n\
bne- 1b"
SMP_ISYNC
: "=&r" (t)
: "r" (&v->counter)
: "cc", "memory");
return t;
}
static __inline__ void atomic_dec(atomic_t *v)
{
int t;
__asm__ __volatile__(
"1: lwarx %0,0,%2 # atomic_dec\n\
addic %0,%0,-1\n\
stwcx. %0,0,%2\n\
bne- 1b"
: "=&r" (t), "=m" (v->counter)
: "r" (&v->counter), "m" (v->counter)
: "cc");
}
static __inline__ int atomic_dec_return(atomic_t *v)
{
int t;
__asm__ __volatile__(
"1: lwarx %0,0,%1 # atomic_dec_return\n\
addic %0,%0,-1\n\
stwcx. %0,0,%1\n\
bne- 1b"
SMP_ISYNC
: "=&r" (t)
: "r" (&v->counter)
: "cc", "memory");
return t;
}
#define atomic_sub_and_test(a, v) (atomic_sub_return((a), (v)) == 0)
#define atomic_dec_and_test(v) (atomic_dec_return((v)) == 0)
/*
* Atomically test *v and decrement if it is greater than 0.
* The function returns the old value of *v minus 1.
*/
static __inline__ int atomic_dec_if_positive(atomic_t *v)
{
int t;
__asm__ __volatile__(
"1: lwarx %0,0,%1 # atomic_dec_if_positive\n\
addic. %0,%0,-1\n\
blt- 2f\n\
stwcx. %0,0,%1\n\
bne- 1b"
SMP_ISYNC
"\n\
2:" : "=&r" (t)
: "r" (&v->counter)
: "cc", "memory");
return t;
}
/*
* Memory barrier.
* The sync instruction guarantees that all memory accesses initiated
* by this processor have been performed (with respect to all other
* mechanisms that access memory). The eieio instruction is a barrier
* providing an ordering (separately) for (a) cacheable stores and (b)
* loads and stores to non-cacheable memory (e.g. I/O devices).
*
* mb() prevents loads and stores being reordered across this point.
* rmb() prevents loads being reordered across this point.
* wmb() prevents stores being reordered across this point.
*
* We can use the eieio instruction for wmb, but since it doesn't
* give any ordering guarantees about loads, we have to use the
* stronger but slower sync instruction for mb and rmb.
*/
#define mb() __asm__ __volatile__ ("sync" : : : "memory")
#define rmb() __asm__ __volatile__ ("sync" : : : "memory")
#define wmb() __asm__ __volatile__ ("eieio" : : : "memory")
#define IATOMIC_DEFINED 1
#endif /* __powerpc__ */
#ifdef __mips__
typedef struct { volatile int counter; } atomic_t;
#define ATOMIC_INIT(i) { (i) }
/*
* atomic_read - read atomic variable
* @v: pointer of type atomic_t
*
* Atomically reads the value of @v. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
#define atomic_read(v) ((v)->counter)
/*
* atomic_set - set atomic variable
* @v: pointer of type atomic_t
* @i: required value
*
* Atomically sets the value of @v to @i. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
#define atomic_set(v,i) ((v)->counter = (i))
/*
* for MIPS II and better we can use ll/sc instruction, and kernel 2.4.3+
* will emulate it on MIPS I.
*/
/*
* atomic_add - add integer to atomic variable
* @i: integer value to add
* @v: pointer of type atomic_t
*
* Atomically adds @i to @v. Note that the guaranteed useful range
* of an atomic_t is only 24 bits.
*/
extern __inline__ void atomic_add(int i, atomic_t * v)
{
unsigned long temp;
__asm__ __volatile__(
".set push \n"
".set mips2 \n"
"1: ll %0, %1 # atomic_add\n"
" addu %0, %2 \n"
" sc %0, %1 \n"
" beqz %0, 1b \n"
".set pop \n"
: "=&r" (temp), "=m" (v->counter)
: "Ir" (i), "m" (v->counter));
}
/*
* atomic_sub - subtract the atomic variable
* @i: integer value to subtract
* @v: pointer of type atomic_t
*
* Atomically subtracts @i from @v. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
extern __inline__ void atomic_sub(int i, atomic_t * v)
{
unsigned long temp;
__asm__ __volatile__(
".set push \n"
".set mips2 \n"
"1: ll %0, %1 # atomic_sub\n"
" subu %0, %2 \n"
" sc %0, %1 \n"
" beqz %0, 1b \n"
".set pop \n"
: "=&r" (temp), "=m" (v->counter)
: "Ir" (i), "m" (v->counter));
}
/*
* Same as above, but return the result value
*/
extern __inline__ int atomic_add_return(int i, atomic_t * v)
{
unsigned long temp, result;
__asm__ __volatile__(
".set push # atomic_add_return\n"
".set noreorder \n"
".set mips2 \n"
"1: ll %1, %2 \n"
" addu %0, %1, %3 \n"
" sc %0, %2 \n"
" beqz %0, 1b \n"
" addu %0, %1, %3 \n"
".set pop \n"
: "=&r" (result), "=&r" (temp), "=m" (v->counter)
: "Ir" (i), "m" (v->counter)
: "memory");
return result;
}
extern __inline__ int atomic_sub_return(int i, atomic_t * v)
{
unsigned long temp, result;
__asm__ __volatile__(
".set push \n"
".set mips2 \n"
".set noreorder # atomic_sub_return\n"
"1: ll %1, %2 \n"
" subu %0, %1, %3 \n"
" sc %0, %2 \n"
" beqz %0, 1b \n"
" subu %0, %1, %3 \n"
".set pop \n"
: "=&r" (result), "=&r" (temp), "=m" (v->counter)
: "Ir" (i), "m" (v->counter)
: "memory");
return result;
}
#define atomic_dec_return(v) atomic_sub_return(1,(v))
#define atomic_inc_return(v) atomic_add_return(1,(v))
/*
* atomic_sub_and_test - subtract value from variable and test result
* @i: integer value to subtract
* @v: pointer of type atomic_t
*
* Atomically subtracts @i from @v and returns
* true if the result is zero, or false for all
* other cases. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
#define atomic_sub_and_test(i,v) (atomic_sub_return((i), (v)) == 0)
/*
* atomic_inc_and_test - increment and test
* @v: pointer of type atomic_t
*
* Atomically increments @v by 1
* and returns true if the result is zero, or false for all
* other cases. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
#define atomic_inc_and_test(v) (atomic_inc_return(1, (v)) == 0)
/*
* atomic_dec_and_test - decrement by 1 and test
* @v: pointer of type atomic_t
*
* Atomically decrements @v by 1 and
* returns true if the result is 0, or false for all other
* cases. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
#define atomic_dec_and_test(v) (atomic_sub_return(1, (v)) == 0)
/*
* atomic_inc - increment atomic variable
* @v: pointer of type atomic_t
*
* Atomically increments @v by 1. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
#define atomic_inc(v) atomic_add(1,(v))
/*
* atomic_dec - decrement and test
* @v: pointer of type atomic_t
*
* Atomically decrements @v by 1. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*/
#define atomic_dec(v) atomic_sub(1,(v))
/*
* atomic_add_negative - add and test if negative
* @v: pointer of type atomic_t
* @i: integer value to add
*
* Atomically adds @i to @v and returns true
* if the result is negative, or false when
* result is greater than or equal to zero. Note that the guaranteed
* useful range of an atomic_t is only 24 bits.
*
* Currently not implemented for MIPS.
*/
#define mb() \
__asm__ __volatile__( \
"# prevent instructions being moved around\n\t" \
".set\tnoreorder\n\t" \
"# 8 nops to fool the R4400 pipeline\n\t" \
"nop;nop;nop;nop;nop;nop;nop;nop\n\t" \
".set\treorder" \
: /* no output */ \
: /* no input */ \
: "memory")
#define rmb() mb()
#define wmb() mb()
#define IATOMIC_DEFINED 1
#endif /* __mips__ */
#ifdef __arm__
/*
* FIXME: bellow code is valid only for SA11xx
*/
/*
* Save the current interrupt enable state & disable IRQs
*/
#define local_irq_save(x) \
({ \
unsigned long temp; \
__asm__ __volatile__( \
"mrs %0, cpsr @ local_irq_save\n" \
" orr %1, %0, #128\n" \
" msr cpsr_c, %1" \
: "=r" (x), "=r" (temp) \
: \
: "memory"); \
})
/*
* restore saved IRQ & FIQ state
*/
#define local_irq_restore(x) \
__asm__ __volatile__( \
"msr cpsr_c, %0 @ local_irq_restore\n" \
: \
: "r" (x) \
: "memory")
#define __save_flags_cli(x) local_irq_save(x)
#define __restore_flags(x) local_irq_restore(x)
typedef struct { volatile int counter; } atomic_t;
#define ATOMIC_INIT(i) { (i) }
#define atomic_read(v) ((v)->counter)
#define atomic_set(v,i) (((v)->counter) = (i))
static __inline__ void atomic_add(int i, volatile atomic_t *v)
{
unsigned long flags;
__save_flags_cli(flags);
v->counter += i;
__restore_flags(flags);
}
static __inline__ void atomic_sub(int i, volatile atomic_t *v)
{
unsigned long flags;
__save_flags_cli(flags);
v->counter -= i;
__restore_flags(flags);
}
static __inline__ void atomic_inc(volatile atomic_t *v)
{
unsigned long flags;
__save_flags_cli(flags);
v->counter += 1;
__restore_flags(flags);
}
static __inline__ void atomic_dec(volatile atomic_t *v)
{
unsigned long flags;
__save_flags_cli(flags);
v->counter -= 1;
__restore_flags(flags);
}
static __inline__ int atomic_dec_and_test(volatile atomic_t *v)
{
unsigned long flags;
int result;
__save_flags_cli(flags);
v->counter -= 1;
result = (v->counter == 0);
__restore_flags(flags);
return result;
}
static inline int atomic_add_negative(int i, volatile atomic_t *v)
{
unsigned long flags;
int result;
__save_flags_cli(flags);
v->counter += i;
result = (v->counter < 0);
__restore_flags(flags);
return result;
}
static __inline__ void atomic_clear_mask(unsigned long mask, unsigned long *addr)
{
unsigned long flags;
__save_flags_cli(flags);
*addr &= ~mask;
__restore_flags(flags);
}
#define mb() __asm__ __volatile__ ("" : : : "memory")
#define rmb() mb()
#define wmb() mb()
#define IATOMIC_DEFINED 1
#endif /* __arm__ */
#ifndef IATOMIC_DEFINED
/*
* non supported architecture.
*/
#warning "Atomic operations are not supported on this architecture."
typedef struct { volatile int counter; } atomic_t;
#define ATOMIC_INIT(i) { (i) }
#define atomic_read(v) ((v)->counter)
#define atomic_set(v,i) (((v)->counter) = (i))
#define atomic_add(i,v) (((v)->counter) += (i))
#define atomic_sub(i,v) (((v)->counter) -= (i))
#define atomic_inc(v) (((v)->counter)++)
#define atomic_dec(v) (((v)->counter)--)
#define mb()
#define rmb()
#define wmb()
#define IATOMIC_DEFINED 1
#endif /* IATOMIC_DEFINED */
/*
* Atomic read/write
* Copyright (c) 2001 by Abramo Bagnara <abramo@alsa-project.org>
*/
/* Max number of times we must spin on a spin-lock calling sched_yield().
After MAX_SPIN_COUNT iterations, we put the calling thread to sleep. */
#ifndef MAX_SPIN_COUNT
#define MAX_SPIN_COUNT 50
#endif
/* Duration of sleep (in nanoseconds) when we can't acquire a spin-lock
after MAX_SPIN_COUNT iterations of sched_yield().
This MUST BE > 2ms.
(Otherwise the kernel does busy-waiting for real-time threads,
giving other threads no chance to run.) */
#ifndef SPIN_SLEEP_DURATION
#define SPIN_SLEEP_DURATION 2000001
#endif
typedef struct {
unsigned int begin, end;
} snd_atomic_write_t;
typedef struct {
volatile const snd_atomic_write_t *write;
unsigned int end;
} snd_atomic_read_t;
void snd_atomic_read_wait(snd_atomic_read_t *t);
static inline void snd_atomic_write_init(snd_atomic_write_t *w)
{
w->begin = 0;
w->end = 0;
}
static inline void snd_atomic_write_begin(snd_atomic_write_t *w)
{
w->begin++;
wmb();
}
static inline void snd_atomic_write_end(snd_atomic_write_t *w)
{
wmb();
w->end++;
}
static inline void snd_atomic_read_init(snd_atomic_read_t *r, snd_atomic_write_t *w)
{
r->write = w;
}
static inline void snd_atomic_read_begin(snd_atomic_read_t *r)
{
r->end = r->write->end;
rmb();
}
static inline int snd_atomic_read_ok(snd_atomic_read_t *r)
{
rmb();
return r->end == r->write->begin;
}
#endif /* __ALSA_IATOMIC_H */
/*
* File: atomic.c
*/
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include "iatomic.h"
#ifdef ATOMIC_TEST
atomic_t g_count = {0};
static void* thread_inc(void* param)
{
int i = 0;
for(i = 0; i < 1000000; i++){
atomic_inc(&g_count);
}
return NULL;
}
static void* thread_dec(void* param)
{
int i = 0;
for(i = 0; i < 1000000; i++){
atomic_dec(&g_count);
}
return NULL;
}
int main(int argc, char* argv[])
{
pthread_t inc_tid = 0;
pthread_t dec_tid = 0;
pthread_create(&inc_tid, NULL, thread_inc, NULL);
pthread_create(&dec_tid, NULL, thread_dec, NULL);
pthread_join(inc_tid, NULL);
pthread_join(dec_tid, NULL);
printf("count=%d\n", g_count.counter);
return 0;
}
#endif/*ATOMIC_TEST*/
all:
gcc -g -m32 fifo_ring.c -DFIFO_RING_TEST -o fifo_ring_test -lpthread
gcc -g -m32 swmr_dlist.c dlist.c -DSWMR_DLIST_TEST -o swmr_dlist_test -lpthread
gcc -g -m32 atomic.c -lpthread -DATOMIC_TEST -o atomic_test
clean:
rm -f *test