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

Reply via email to