This is the new home of the egghelp.org community forum.
All data has been migrated (including user logins/passwords) to a new phpBB version.


For more information, see this announcement post. Click the X in the top right-corner of this box to dismiss this message.

modules and multi-threading

Discussion of Eggdrop's code and module programming in C.
K
Kappa007
Voice
Posts: 38
Joined: Tue Jul 26, 2005 9:53 pm

modules and multi-threading

Post by Kappa007 »

Anyone done that already?

Just curious cause since eggdrop itself is not thread-safe, output seems to be a bit cumbersome:
Either you wrap some mutex around the stuff you need or you do some queue which gets polled by some hook (secondly).

Anyone seen/used any other approach?



P.S.: Is there some thread-safe implementation of TCL avail?
User avatar
demond
Revered One
Posts: 3073
Joined: Sat Jun 12, 2004 9:58 am
Location: San Francisco, CA
Contact:

Post by demond »

I fail to see a rationale in that

eggdrop is inherently asynchronous, event-driven I/O application and as such, it could hardly benefit from multithreading

maybe a multiserver eggdrop would be better implemented spawning threads (since you have to respond independently, in real time, to events on multiple servers); I thought about starting such project but it would require dedicating a significant amount of my free time (that MT stuff is tricky & hard to get right), too much for a hobby... maybe some day if I'm out of work hehe

of course, I'd applaud you for undertaking an endeavour like this :) I'm less than impressed with current direction of eggdrop development (javascript/perl scripting etc. - provided they ever complete that, which is highly unlikely, given their pace)
connection, sharing, dcc problems? click <here>
before asking for scripting help, read <this>
use

Code: Select all

 tag when posting logs, code
K
Kappa007
Voice
Posts: 38
Joined: Tue Jul 26, 2005 9:53 pm

Post by Kappa007 »

It's not about eggdrop benefiting for that kind of multi-threading:

Lets say you want to perform some synchronous I/O in your module or run a lengthy operation.
I assume the bot would be blocked till the call to your module returned.
Easiest way to solve that is to use threads; another way would be to make your module only use async I/O aswell which is not allways possible/desired.
If you use threads then you cannot just call eggdrop's output routines, the module interface functions are just missing locks.

That's my whole issue.
User avatar
demond
Revered One
Posts: 3073
Joined: Sat Jun 12, 2004 9:58 am
Location: San Francisco, CA
Contact:

Post by demond »

the only such realistic scenario I can think of is accessing a database, but if it takes *that* long you were forced to handle it in separate thread, there is something wrong with your db design anyway
connection, sharing, dcc problems? click <here>
before asking for scripting help, read <this>
use

Code: Select all

 tag when posting logs, code
User avatar
sKy
Op
Posts: 194
Joined: Thu Apr 14, 2005 5:58 pm
Location: Germany

Post by sKy »

Ture, eggdrop isn`t mulitthread safe by standard. But it`s possible.

I am using eggdrop 1.6.17 with tcl 8.4.11. The tcl need to be compiled with --enable-threads

When compiling eggdrop you will have to use the tcl version with enabled threads too. Dunno, but i used
./configure --enable-threads

Then install a threading package. I use this here. Works fine.
http://mkextensions.sourceforge.net/ mkThreads 1.2
socketapi | Code less, create more.
K
Kappa007
Voice
Posts: 38
Joined: Tue Jul 26, 2005 9:53 pm

Post by Kappa007 »

However there is an important restriction:

Code: Select all

An important constraint of the Tcl threads implementation is that only the thread that created a Tcl interpreter can use that interpreter. In other words, multiple threads can not access the same Tcl interpreter. (However, a single thread can safely create and use multiple interpreters.) 
So a threaded Tcl does not help at all when one wishes to access the Tcl-Iterpreter used by Eggdrop in a multi-threaded module (which is what i wanted to do).

I now push my Tcl commands to a thread-safe queue and do a secondly hook which pops the commands from the queue and executes them in the eggdrop main thread.

Regards,
Kappa
User avatar
sKy
Op
Posts: 194
Joined: Thu Apr 14, 2005 5:58 pm
Location: Germany

Post by sKy »

Sorry, I don`t know about modules. :/

I handle this as:
Eggdrop is "globalest level". From there i create a main thread. From the main thread i create all other threads.

By using the tvar command i can:
- show info messages
- show errormessages
- ussing callbacks to the globalest level or other threads

You just write a tvar variable and check and process this on the globalest level. From there you can callback other threads.

This should be a workarround for the code quote you posted. Hope i did get you right and not claim on the wrong three.
socketapi | Code less, create more.
K
Kappa007
Voice
Posts: 38
Joined: Tue Jul 26, 2005 9:53 pm

Post by Kappa007 »

Ehm no don't think that's a workaround for the quoted statement.
What you say may be perfectly right for Tcl scripts...just got nothing to do with modules or the interpreter itself...
That is unless i got something terribly wrong 8)

You refer to the usage of threads "inside" a tcl interpreter, but the quoted text says that access to a Tcl_Iterp struct is only safe when done from the thread that created it (using Tcl_CreateInterp()).

This means that you cannot use any Tcl_Eval/Set/Get/... functions on the eggdrop tcl interpreter from another thread.
If one would like to do so that would require guarding the interpreter struct with some synchronization primitive (e.g. a mutex).
User avatar
De Kus
Revered One
Posts: 1361
Joined: Sun Dec 15, 2002 11:41 am
Location: Germany

Post by De Kus »

if you want to run a modular task threaded, just run a new process by running either the TCL interpretator or directly the compiled executable. Communication will be done via sockets (redirecting stdin/out). This is the way I use my server status (port scanner) script which might be ready in 1s or take 2min. Of course thats not the solution for modules like stats or seens where the data is stored within a huge memory table. But piping data up to ~1mb should be fast enough.
De Kus
StarZ|De_Kus, De_Kus or DeKus on IRC
Copyright © 2005-2009 by De Kus - published under The MIT License
Love hurts, love strengthens...
K
Kappa007
Voice
Posts: 38
Joined: Tue Jul 26, 2005 9:53 pm

Post by Kappa007 »

Yeah, that's also a nice idea to handle that topic.
Didn't even think about that :)

Ohh just by the way here's the code I currently use to dispatch Tcl commands (just in case anyone cares).

Might be incomplete and missing some #includes, hope no mem-leaks, just a quick copy&paste... 8)

Code: Select all

/* mymodule.c */

static void TclCommandQueueHook()
{    
    CheckTclCommandQueue();    
}

char* mymodule_start(Function* global_funcs)
{
    /* ... */
    InitTclCommandQueue();
    add_hook (HOOK_SECONDLY, (Function)TclCommandQueueHook);
    /* ... */
}

Code: Select all

/* cmdqueue.h */

#ifdef __cplusplus
extern "C"
{
#endif
    int InitTclCommandQueue();
    void DispatchTclCommand( const char* aCommand );
    void CheckTclCommandQueue();

#ifdef __cplusplus
}
#endif

Code: Select all

/* cmdqueue.c */

struct CmdQueueEntry
{
	char* Command;
	struct CmdQueueEntry* Next;
};

struct CmdQueueEntry* CmdQueueStart = NULL;
struct CmdQueueEntry* CmdQueueEnd   = NULL;

/* mutex for the command queue */
pthread_mutex_t*    TclCommandQueueGuard = NULL;

int InitTclCommandQueue()
{
    pthread_mutexattr_t aMutexAttribute;
    pthread_mutexattr_init( &aMutexAttribute );
    pthread_mutexattr_setpshared( &aMutexAttribute, 0 );
    pthread_mutexattr_settype( &aMutexAttribute, PTHREAD_MUTEX_RECURSIVE );

    // initialize mutex
    TclCommandQueueGuard = nmalloc(sizeof( pthread_mutex_t ));
    if (pthread_mutex_init( TclCommandQueueGuard, &aMutexAttribute ) != 0)
    {
        TclCommandQueueGuard = NULL;
        return -1;
    }
    return 0;
}

void DispatchTclCommand( const char* aCommand )
{
	if (TclCommandQueueGuard == NULL) return;		

	pthread_mutex_lock( TclCommandQueueGuard );
	{
		struct CmdQueueEntry* aNewCommand = nmalloc( sizeof( struct CmdQueueEntry ) );
		aNewCommand->Command = nmalloc( strlen( aCommand ) + 1 );
		strncpy( aNewCommand->Command, aCommand, strlen(aCommand) );
		aNewCommand->Command[strlen( aCommand )] = '\0';
		aNewCommand->Next = NULL;

		// no end set yet?
		if (CmdQueueEnd == NULL)
		{
			// start from the beginning
			struct CmdQueueEntry* Next = CmdQueueStart;
			if (Next != NULL)
			{
				while (Next->Next != NULL)
				{
					Next = Next->Next;
				}
				CmdQueueEnd = Next;
			}
			// no start yet?
			else
			{
				CmdQueueStart = aNewCommand;
				CmdQueueEnd = aNewCommand;
			}
		}
		else
		{
			CmdQueueEnd->Next = aNewCommand;
			CmdQueueEnd = aNewCommand;
		}
	}
	pthread_mutex_unlock( TclCommandQueueGuard );
}	

void CheckTclCommandQueue()
{
	if (TclCommandQueueGuard == NULL) return;		

	pthread_mutex_lock( TclCommandQueueGuard );
	{
		while (CmdQueueStart != NULL)
		{
			if (CmdQueueStart->Command != NULL)
			{
				Tcl_EvalEx( interp, CmdQueueStart->Command, -1, TCL_EVAL_GLOBAL );
				nfree( CmdQueueStart->Command );
			}			
			struct CmdQueueEntry* Next = CmdQueueStart->Next;
			nfree( CmdQueueStart );
			CmdQueueStart = Next;
		}
		CmdQueueEnd = CmdQueueStart;
	}
	pthread_mutex_unlock( TclCommandQueueGuard );
}
User avatar
demond
Revered One
Posts: 3073
Joined: Sat Jun 12, 2004 9:58 am
Location: San Francisco, CA
Contact:

Post by demond »

Kappa007 wrote: Ohh just by the way here's the code I currently use to dispatch Tcl commands (just in case anyone cares).
I'd care if your efforts serve a purpose other than exercising your MT skills - that is, an eggdrop purpose (and you are able to demonstrate how eggdrop benefits from that)
connection, sharing, dcc problems? click <here>
before asking for scripting help, read <this>
use

Code: Select all

 tag when posting logs, code
K
Kappa007
Voice
Posts: 38
Joined: Tue Jul 26, 2005 9:53 pm

Post by Kappa007 »

Well benefits are: you can use (foreign) code which uses blocking calls in your modules w/o having to worry about your eggdrop beeing stalled.
And that's enough for me :)

And if that does not count for you then it's just about the possibility to use them at all :P
User avatar
De Kus
Revered One
Posts: 1361
Joined: Sun Dec 15, 2002 11:41 am
Location: Germany

Post by De Kus »

Kappa007 wrote:Well benefits are: you can use (foreign) code which uses blocking calls in your modules w/o having to worry about your eggdrop beeing stalled.
And that's enough for me :)
Did you read my post? Its absoludly possible to run asyncron tasks. Btw. the reason eggdrop is so popular is, because its single threaded. That way shells can restrict eggdrop shells to 1 bg process. For further background tasks possible. Furthermore 1 bg process eggdrop = 1 IRC connection. For hosting on a general shell account with multiple back processes and connections allowed it might be usefull, but that wouldnt be all. Eggdrop is and will always keep single threaded and single server for these reasons. You can however still fork into a forground thread to execute async calls as mentioned above by me.
De Kus
StarZ|De_Kus, De_Kus or DeKus on IRC
Copyright © 2005-2009 by De Kus - published under The MIT License
Love hurts, love strengthens...
K
Kappa007
Voice
Posts: 38
Joined: Tue Jul 26, 2005 9:53 pm

Post by Kappa007 »

Ehhh yes I read it, i even replied in case you didn't notice...
Kappa007 wrote: Yeah, that's also a nice idea to handle that topic.
Didn't even think about that :)
User avatar
demond
Revered One
Posts: 3073
Joined: Sat Jun 12, 2004 9:58 am
Location: San Francisco, CA
Contact:

Post by demond »

Kappa007 wrote:Well benefits are: you can use (foreign) code which uses blocking calls in your modules w/o having to worry about your eggdrop beeing stalled.
And that's enough for me :)

And if that does not count for you then it's just about the possibility to use them at all :P
well, you're still on the proof-of-concept stage

I'd applaud if you roll out, say, a non-blocking mysql module

and I'd admire you for producing multi-server MT eggdrop
connection, sharing, dcc problems? click <here>
before asking for scripting help, read <this>
use

Code: Select all

 tag when posting logs, code
Post Reply