blob: c832aa9cac2eb9e850ecf5334fdc2e4fbd8f1c9d [file] [log] [blame]
#ifndef PLS_H
#define PLS_H
/**
* Copyright (c) 2019, The Linux Foundation. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above
* copyright notice, this list of conditions and the following
* disclaimer in the documentation and/or other materials provided
* with the distribution.
* * Neither the name of The Linux Foundation nor the names of its
* contributors may be used to endorse or promote products derived
* from this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
* BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
* OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
* IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <stdlib.h>
#include "AEEStdDef.h"
#include "AEEatomic.h"
#include "verify.h"
#include "HAP_farf.h"
struct PLS;
struct plskey {
uintptr_t type;
uintptr_t key;
};
struct PLS {
struct PLS* next;
struct plskey key;
void (*dtor)(void* data);
uint64_t data[1];
};
struct pls_table {
struct PLS* lst;
uint32_t uRefs;
uint32_t primThread;
};
/**
* initialize on every thread and stick the pls_thread_deinit
* function into the threads tls
*/
static __inline int pls_thread_init(struct pls_table* me, uintptr_t tid) {
if(tid == me->primThread) {
return 0;
}
if(0 == atomic_CompareOrAdd(&me->uRefs, 0, 1)) {
return -1;
}
return 0;
}
/* call this constructor before the first thread creation with the
* first threads id
*/
static __inline void pls_ctor(struct pls_table* me, uintptr_t primThread) {
me->uRefs = 1;
me->primThread = primThread;
}
static __inline struct pls_table* pls_thread_deinit(struct pls_table* me) {
if(me && 0 != me->uRefs && 0 == atomic_Add(&me->uRefs, -1)) {
struct PLS* lst, *next;
lst = me->lst;
me->lst = 0;
while(lst) {
next = lst->next;
if(lst->dtor) {
FARF(HIGH, "pls dtor %p", lst->dtor);
lst->dtor((void*)lst->data);
}
free(lst);
lst = next;
}
return me;
}
return 0;
}
/**
* adds a new key to the local storage, overriding
* any previous value at the key. Overriding the key
* does not cause the destructor to run.
*
* @param type, type part of the key to be used for lookup,
these should be static addresses.
* @param key, the key to be used for lookup
* @param size, the size of the data
* @param ctor, constructor that takes a context and memory of size
* @param ctx, constructor context passed as the first argument to ctor
* @param dtor, destructor to run at pls shutdown
* @param ppo, output data
* @retval, 0 for success
*/
static __inline int pls_add(struct pls_table* me, uintptr_t type, uintptr_t key, int size, int (*ctor)(void* ctx, void* data), void* ctx, void (*dtor)(void* data), void** ppo) {
int nErr = 0;
struct PLS* pls = 0;
struct PLS* prev;
VERIFY(me->uRefs != 0);
VERIFY(0 != (pls = (struct PLS*)calloc(1, size + sizeof(*pls) - sizeof(pls->data))));
if(ctor) {
VERIFY(0 == ctor(ctx, (void*)pls->data));
}
pls->dtor = dtor;
pls->key.type = type;
pls->key.key = key;
do {
pls->next = me->lst;
prev = (struct PLS*)atomic_CompareAndExchangeUP((uintptr_t*)&me->lst, (uintptr_t)pls, (uintptr_t)pls->next);
} while(prev != pls->next);
if(ppo) {
*ppo = (void*)pls->data;
}
FARF(HIGH, "pls added %p", dtor);
bail:
if(nErr && pls) {
free(pls);
}
return nErr;
}
static __inline int pls_lookup(struct pls_table* me, uintptr_t type, uintptr_t key, void** ppo);
/**
* like add, but will only add 1 item if two threads try to add at the same time. returns
* item if its already there, otherwise tries to add.
* ctor may be called twice
* callers should avoid calling pls_add which will override the singleton
*/
static __inline int pls_add_lookup_singleton(struct pls_table* me, uintptr_t type, uintptr_t key, int size, int (*ctor)(void* ctx, void* data), void* ctx, void (*dtor)(void* data), void** ppo) {
int nErr = 0;
struct PLS* pls = 0;
struct PLS* prev;
if(0 == pls_lookup(me, type, key, ppo)) {
return 0;
}
VERIFY(me->uRefs != 0);
VERIFY(0 != (pls = (struct PLS*)calloc(1, size + sizeof(*pls) - sizeof(pls->data))));
if(ctor) {
VERIFY(0 == ctor(ctx, (void*)pls->data));
}
pls->dtor = dtor;
pls->key.type = type;
pls->key.key = key;
do {
pls->next = me->lst;
if(0 == pls_lookup(me, type, key, ppo)) {
if(pls->dtor) {
pls->dtor((void*)pls->data);
}
free(pls);
return 0;
}
prev = (struct PLS*)atomic_CompareAndExchangeUP((uintptr_t*)&me->lst, (uintptr_t)pls, (uintptr_t)pls->next);
} while(prev != pls->next);
if(ppo) {
*ppo = (void*)pls->data;
}
FARF(HIGH, "pls added %p", dtor);
bail:
if(nErr && pls) {
free(pls);
}
return nErr;
}
/**
* finds the last data pointer added for key to the local storage
*
* @param key, the key to be used for lookup
* @param ppo, output data
* @retval, 0 for success
*/
static __inline int pls_lookup(struct pls_table* me, uintptr_t type, uintptr_t key, void** ppo) {
struct PLS* lst;
for(lst = me->lst; me->uRefs != 0 && lst != 0; lst = lst->next) {
if(lst->key.type == type && lst->key.key == key) {
if(ppo) {
*ppo = lst->data;
}
return 0;
}
}
return -1;
}
#endif //PLS_H