Elektra is not really multithread safe. The reason is not elektra, but its
backends (all available). I have even patched filesys having filelocking
which gives a slightly better failure rate:
[EMAIL PROTECTED]:~/Projekte/Konsole/pthreads> ./elektra 2> /dev/null
******************************
324548 tests total
-315822 tests passed
-----
8726 tests failed (2,69 %)
******************************
(about 2 percent less then without locking)
In the tested szenario it sets and gets all the time the same key, value. So:
Prozess 1 | Prozess2
kdbSetKey |
| kdbSetKey
kdbGetKey |
will lead to correct beheavior.
The problem is following:
kdbSetKey |
| kdbDelKey
kdbGetKey |
and lots of combinations like:
kdbSetKeys |
| kdbGetKeys
kdbSetKeys(cont)
means: that kdbSetKeys is not atomic at all, and it could interrupt exactly
during writing a key.
Summery: No thread saftey if backends are not.
First it would be great having the possibility to del and link keys during a
kdbSetKeys transaction
The other question is, if we should implement a semaphore strategy
(readers/writers with prefering writers) inside filesys or directly in
elektra. Directly would have the advantage that backends do not need any
locking.
A compromise would be that elektra provides a readers_lock and writers_lock
(and unlock) which can be used in backends (if desired).
thank you
Markus Raab
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <pthread.h>
#include <semaphore.h>
#include <kdb.h>
#define INFINITY 100000
#define NUM_THREAD 100
#define ROOT_KEY "user/test"
#define NUL_KEY ROOT_KEY "/nul"
#define NUL_VAL "s"
#define NUL_COM "s"
#define NEW_KEY ROOT_KEY "/new"
#define NEW_VAL "value"
#define NEW_COM "comme"
#define ANO_KEY ROOT_KEY "/ano"
#define ANO_VAL "ano=th;va\nlue"
#define ANO_COM "com=en;tt\nhhi"
#define DIR_KEY ROOT_KEY "/dir"
#define TEST_OK 0
#define TEST_FAIL 1
#define BUF_SIZ 50
void statisticsOut ();
/*Printing Functions for information/errors*/
void bailOut (const char * msg);
void headerOut (const char * msg);
void testOut (Key * orig, Key * read, const char * msg);
/*These are the only functions not using
* the above printing functions. They are
* not used by the testing suite.*/
void printKey (Key * k);
void printKeys (KeySet * set);
/*Get all keys and check if valid*/
void getKey();
void getKeys();
/*Sets all above keys*/
void setKey();
void setKeys();
/*Deletes all above keys*/
void delKeys();
pthread_t preader[NUM_THREAD], pwriter [NUM_THREAD];
int failures;
int tests;
int counter;
void * reader (void * pV_data);
void * writer (void * pV_data);
int in = 0;
int out = 0;
int main ()
{
int i;
for (i=0; i< NUM_THREAD; i++) if (pthread_create (&preader[i], NULL, reader, (void *) 0) != 0) exit (1);
for (i=0; i< NUM_THREAD; i++) if (pthread_create (&pwriter[i], NULL, writer, (void *) 0) != 0) exit (1);
for (i=0; i< NUM_THREAD; i++) pthread_join (preader[i],NULL);
for (i=0; i< NUM_THREAD; i++) pthread_join (pwriter[i],NULL);
statisticsOut();
return 0;
}
/*int main(int argc, char **argv) {
kdbOpen(&handle);
headerOut ("start tests");
headerOut ("single set, single get");
setKey(handle);
getKey();
delKeys();
headerOut ("multiple set, single get");
setKeys ();
getKey ();
delKeys();
headerOut ("single set, multiple get");
setKey();
getKeys();
delKeys();
headerOut ("multiple set, multiple get");
setKeys ();
getKeys ();
delKeys ();
headerOut ("finished tests");
kdbClose(&handle);
return 0;
}*/
#define NR 1000
void * reader (void * pV_data)
{
int i;
KDBHandle h;
kdbOpen (&h);
KeySet * set = ksNew ();
for (i=0; i<NR; i++)
{
setKeys (h);
getKeys (h);
/*printf ("reader\n");
fflush (stdout);*/
}
kdbClose (&h);
return (NULL);
}
void * writer (void * pV_data)
{
int i;
KDBHandle h;
kdbOpen (&h);
for (i=0; i< NR; i++)
{
setKey(h);
getKey(h);
/*printf ("writer\n");
fflush (stdout);*/
}
kdbClose (&h);
return (NULL);
}
/**Return new various keys
* These Keys will be set and get in various
* tests. It will be evaluated if the backend
* does everything correctly.*/
Key * newNulKey ()
{
return
keyNew (NUL_KEY,
KEY_SWITCH_VALUE, NUL_VAL,
KEY_SWITCH_COMMENT, NUL_COM,
KEY_SWITCH_END);
}
Key * newNewKey ()
{
return
keyNew (NEW_KEY,
KEY_SWITCH_VALUE, NEW_VAL,
KEY_SWITCH_COMMENT, NEW_COM,
KEY_SWITCH_END);
}
Key * newAnoKey ()
{
return
keyNew (ANO_KEY,
KEY_SWITCH_VALUE, ANO_VAL,
KEY_SWITCH_COMMENT, ANO_COM,
KEY_SWITCH_END);
}
Key * newDirKey ()
{
return
keyNew (DIR_KEY,
KEY_SWITCH_TYPE, KEY_TYPE_DIR);
}
/**Prints header for failures*/
void bailOut (const char * msg)
{
fprintf (stderr, "******** %29s ********\n", msg);
//TODO:
kdbPrintError (msg);
// perror (msg);
// exit (0);
}
/**Prints header for what is done now*/
void headerOut (const char * msg)
{
fprintf (stderr, "-------- %29s --------\n", msg);
}
void testOut (Key * orig, Key * read, const char * keyname)
{
uint32_t failed=0;
fprintf (stderr, "Test ");
tests ++;
failed=keyCompare (orig, read);
if (failed == KEY_SWITCH_NEEDSYNC)
{
fprintf (stderr, "succeded for : %s\n", keyname);
// return;
} else {
fprintf (stderr, "failed for : %s\n", keyname);
failures ++;
}
if (failed & KEY_SWITCH_TYPE)
fprintf (stderr, "type differs: is %d, was %d\n",
keyGetType(read),keyGetType(orig));
if (failed & KEY_SWITCH_NAME)
fprintf (stderr, "name differs: is \"%s\", was \"%s\"\n",
keyStealName(read),keyStealName(orig));
if (failed & KEY_SWITCH_VALUE)
fprintf (stderr, "value differs: is \"%s\", was \"%s\"\n",
(char *) keyStealValue(read), (char *) keyStealValue(orig));
if (failed & KEY_SWITCH_OWNER)
fprintf (stderr, "owner differs: is \"%s\", was \"%s\"\n",
keyStealOwner(read),keyStealOwner(orig));
if (failed & KEY_SWITCH_COMMENT)
fprintf (stderr, "comment differs: is \"%s\", was \"%s\"\n",
keyStealComment(read),keyStealComment(orig));
if (failed & KEY_SWITCH_UID)
fprintf (stderr, "uid differs: is \"%d\", was \"%d\"\n",
keyGetUID(read),keyGetUID(orig));
if (failed & KEY_SWITCH_GID)
fprintf (stderr, "gid differs: is \"%d\", was \"%d\"\n",
keyGetGID(read),keyGetGID(orig));
if (failed & KEY_SWITCH_MODE)
fprintf (stderr, "mode differs: is \"0%o\", was \"0%o\"\n",
keyGetAccess(read),keyGetAccess(orig));
if (failed & KEY_SWITCH_FLAG)
fprintf (stderr, "flag differs\n");
keyDel (orig);
}
void statisticsOut ()
{
printf ("\n******************************\n");
printf (" %d tests total\n", tests);
printf ("-%d tests passed\n", tests-failures);
printf ("-----\n");
printf (" %d tests failed", failures);
if (failures != 0) printf (" (%3.2f %%)", ((double)failures/(double)tests)*100.0);
printf ("\n******************************\n");
}
void printKey (Key * k)
{
size_t s;
char * str;
size_t c;
char * com;
size_t n;
char * nam;
n = keyGetNameSize (k);
nam = (char*) malloc (n);
if (nam == NULL) {bailOut ("malloc error");}
keyGetName (k, nam, n);
s = keyGetDataSize (k);
str = (char*) malloc (s+100);
if (str == NULL) {bailOut ("malloc error");}
keyGetString (k, str, s);
c = keyGetCommentSize (k);
com = (char*) malloc (c+100);
if (com == NULL) {bailOut ("malloc error");}
keyGetComment (k, com, c);
printf ("Name[%d,%d]: %s\t", n, strblen(nam), nam);
printf ("String[%d,%d]: %s\t", s, strblen(str), str);
printf ("Kommentar[%d,%d]: %s\n", c, strblen(com), com);
free (nam);
free (str);
free (com);
}
void printKeys (KeySet * set)
{
Key * k;
ksRewind (set);
k = ksNext (set);
while (k) {
printKey (k);
k = ksNext (set);
}
}
void getKeys (KDBHandle handle)
{
Key * t;
KeySet * set;
/* int ret;
Key * root;
Key *parentKey;
ssize_t rc; */
set = ksNew();
/* Get all value keys for this application */
headerOut ("getChildKeys");
set = ksNew();
if (kdbGetChildKeys (handle, ROOT_KEY, set, KDB_O_RECURSIVE | KDB_O_DIR) == -1)
bailOut("kdbGetChildKeys");
t = ksLookupByName (set, NUL_KEY, 0);
if (t) testOut (newNulKey(), t, NUL_KEY);
else bailOut ("Did not find nulkey");
ksRewind(set);
t = ksLookupByName (set, NEW_KEY, 0);
if (t) testOut (newNewKey(), t, NEW_KEY);
else bailOut ("Did not find newkey");
ksRewind(set);
t = ksLookupByName (set, ANO_KEY, 0);
if (t) testOut (newAnoKey(), t, ANO_KEY);
else bailOut ("Did not find anokey");
ksRewind(set);
t = ksLookupByName (set, DIR_KEY, 0);
if (t) testOut (newDirKey(), t, DIR_KEY);
else bailOut ("Did not find dirkey");
ksDel (set);
}
void getKey(KDBHandle handle)
{
Key * k;
headerOut ("getKey");
k = keyNew(NUL_KEY, KEY_SWITCH_END);
if (kdbGetKey (handle, k) == -1) bailOut ("newkey kdbGetKey");
else testOut (newNulKey(), k, NUL_KEY);
keyDel (k);
k = keyNew(NEW_KEY, KEY_SWITCH_END);
if (kdbGetKey (handle, k) == -1) bailOut ("newkey kdbGetKey");
else testOut (newNewKey(), k, NEW_KEY);
keyDel (k);
k = keyNew(ANO_KEY, KEY_SWITCH_END);
if (kdbGetKey (handle, k) == -1) bailOut ("kdbGetKey");
else testOut (newAnoKey(), k, ANO_KEY);
keyDel (k);
k = keyNew(DIR_KEY, KEY_SWITCH_END);
if (kdbGetKey (handle, k) == -1) bailOut ("kdbGetKey");
else testOut (newDirKey(), k, DIR_KEY);
keyDel (k);
}
void setKey (KDBHandle handle)
{
Key * k;
k = newNulKey();
if (kdbSetKey (handle, k) == -1) bailOut ("Error in kdbSetKeys");
keyDel (k);
k = newNewKey();
if (kdbSetKey (handle, k) == -1) bailOut ("Error in kdbSetKeys");
keyDel (k);
k = newAnoKey();
if (kdbSetKey (handle, k) == -1) bailOut ("Error in kdbSetKeys");
keyDel (k);
k = newDirKey();
if (kdbSetKey (handle, k) == -1) bailOut ("Error in kdbSetKeys");
keyDel (k);
}
void setKeys (KDBHandle handle)
{
KeySet * ks = ksNew();
ksAppend (ks, newNulKey());
ksAppend (ks, newNewKey());
ksAppend (ks, newAnoKey());
ksAppend (ks, newDirKey());
if (kdbSetKeys (handle, ks) == -1) bailOut ("Error in kdbSetKeys");
ksDel (ks);
}
/**This removes all the keys*/
void delKeys (KDBHandle handle)
{
headerOut ("Will remove all keys now");
Key * k;
k = keyNew(NUL_KEY, KEY_SWITCH_END);
if (kdbRemoveKey (handle, k) == -1) bailOut ("Error Remove Nul Key");
keyDel (k);
k = keyNew(NEW_KEY, KEY_SWITCH_END);
if (kdbRemoveKey (handle, k) == -1) bailOut ("Error Remove New Key");
keyDel (k);
k = keyNew(ANO_KEY, KEY_SWITCH_END);
if (kdbRemoveKey (handle, k) == -1) bailOut ("Error Remove Ano Key");
keyDel (k);
k = keyNew(DIR_KEY, KEY_SWITCH_END);
if (kdbRemoveKey (handle, k) == -1) bailOut ("Error Remove Dir Key");
keyDel (k);
}
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Registry-list mailing list
[email protected]
https://lists.sourceforge.net/lists/listinfo/registry-list