evnum_incr( )

Aug 3, 2012 at 1:34pm

evnum_incr( )

Hello,

in the SDK :

If you call outlet_int(), outlet_float(), outlet_list(), or outlet_anything() inside a Qelem or during some idle or interrupt time, you should increment Max’s Event Serial Number beforehand.

Is it true for outlet calls in a custom thread (created by the external) ?
More generally is there anything more to take care about for such callback.

Thanks.

#63842
Aug 3, 2012 at 4:16pm

Hi Nicolas.

Looks like you’re doing dangerous stuff… afaik, you are not allowed to do outlet calls from threads other than the main or scheduler thread…

aa

#230232
Aug 3, 2012 at 5:09pm

Hello Andrea,

Gosh, you are right ; (even if it seems to work well) i suppose i need to use a clocked task to avoid strange things.

Ciao.

#230233
Aug 3, 2012 at 5:17pm

Hello,

but what is exactly the meaning of “during some idle or interrupt time” in the phrase quoted above ?

#230234
Aug 3, 2012 at 11:17pm

Wow, this is some quite interesting detail that I missed for years (although I couldn’t report any crash that could have been tracked to this error)…

Thanks anyway for this highlight! And, of course, I’m also wondering if there was some more detailed information about this form Cycling ’74…

Best,
Ádám

#230235
Aug 4, 2012 at 5:22am

Hello,

long time ago i tested to call outlets without qelem/clock and since it seems to work fine i forget to get confirmation there. But now is suspect [makenote] incoherences to result from that ; i thought i can solve it whit evnum_incr( ) (is this number used to track repeated notes ?)… and it seems to work… (but does it really work) ;-)

Of course “detailed information” from cycling74 should be awesome.

Is there a safe way to call an outlet from a custom thread or is it definitively “not allowed” ?

#230236
Aug 4, 2012 at 9:27am

It’s definitely not allowed. Using qelem or clock is the way to go. Makes also sure that you don’t call outlet_* within a thread protected portion of the code (with locks or critical regions).

#230237
Aug 4, 2012 at 10:35am

Hi,

good to know ; thanks for clarification.

Notices for readers in the futur : have a look on “simplethread” example ;-)

#230238
Sep 24, 2012 at 3:17pm

… ok. but so, what does “idle or interrupt time” mean? can someone post an example of evnum_incr – besides the case of qelem?

thanks
zz

#230239
Sep 24, 2012 at 3:18pm

- EDIT: aa ;)

#230240
Oct 24, 2012 at 3:25pm

This evnum_incr() etc. is some seriously old school mac stuff. It is totally obsolete and shouldn’t be in the SDK anymore — thanks for catching it!

For thoroughness, however, “idle time” is when the main thread (aka “the queue”) is serviced and “interrupt time” is when the scheduler (which was activated by an actual hardware interrupt back in the 90′s) is serviced.

Hope you enjoyed opening the time capsule ;-)
Tim

#230241
Oct 26, 2012 at 5:44pm

Hi,

I decided to have a look on my codes and realized that I’m actually doing outlet calls from custom threads quite a lot. Although I never had any crash due to this, I decided to clean up the code and fix this issue. The question: which is faster? Having a qelem and a mutex to read the relevant data from my object, or using defer() with the data that has to be sent out as a parameter? Actually, is it enough if I simply defer() the outlet stuff or is it necessary to use the qelem mechanism? (Since I don’t want any data to be dropped, even if they arrive too fast, I would go for defer(), if possible…)

Thanks,
Ádám

#230242
Oct 27, 2012 at 6:35am

Hi,

I read in this forum that defer( ) mechanism is a bit depreciated. In that case i use a t_clock and a queue. In the custom thread, i append to the queue and then i schedule the clock_task with a zero delay that will throw data to the outlet. Of course you need a mutex in the scenario. That seems to work pretty well and faster enough.

#230243
Oct 27, 2012 at 9:11am

Hi,

thanks for the answer. That sounds too bad; as I understand, this means that (since I need to output data each time and the ordering of the output is important), I’ll need to implement some internal memory manager that keeps track of the order of the messages (and also their content, of course), but, since outlets can’t be mutexed, I’ll need to duplicate the data each time before outputting them. That sounds like a waste of resources… :-(

Anyway, thanks for the comment,
Ádám

#230244
Oct 27, 2012 at 9:36am

Hi Adam,

I might not have a clear picture of what you are trying to do, but why can’t you use a defer_low() ???
If event backlogging is not an issue for you, defer_low() will NOT drop any data and will preserve a message sequence’s order.
Perfect function to be called to output data from custom threads…

- Luigi

P.S.
@Nicolas: where did you read that the defer mechanism was a bit deprecated ?

#230245
Oct 27, 2012 at 9:57am

Hi,

@Ádám : Yep, you are right about the process ; in my case i have profiled the result and the cost is near zero. I prefer clock and qelem tasks. I never use defer or defer_low (except in the object_new method) but that’s just a matter of taste.

#230246
Oct 27, 2012 at 10:00am

Hi,

thanks for comments. So…. Is there a C74 guru who could jump in and clarify whether the defer*() family of functions is considered deprecated or not? Actually, I would prefer to go with that as it is a lot more simple to implement… ;-)

Thanks,
Ádám

#230247
Oct 27, 2012 at 10:43am

Hi,

I should not have used “depreciated” but instead something like “there is situations where qelem is preferred to defer” ;-)

http://cycling74.com/forums/topic.php?id=38419

http://cycling74.com/forums/topic.php?id=34884
http://cycling74.com/forums/topic.php?id=34498

http://cycling74.com/forums/topic.php?id=36065

#230248
Oct 27, 2012 at 10:59am

Ahhh, ok…. thanks for the clarification, Nicolas.

So, I am not a C74 guru, but I think it could be safe to say that the defer mechanism has not been deprecated. However – as Nicolas pointed out – in many situation using qelems is more recommended/appropriate than defer_low().

Having said all this, I believe that in Adam’s situation he really wants a defer_low() because of two main reasons I see:

1 – He doesn’t want any data to be dropped. (The qelem will drop messages if it gets flooded with more messages than can be serviced at a given time)

2 – As he already stated, It’s way simpler to implement.

Hope all is clear now.

- Luigi

#230249
Oct 27, 2012 at 1:33pm

Hi,

@Luigi : Déprécier (french) / depreciated / deprecated ; oops : deceptive cognates ;-)

@Ádám : I’m not sure that the use of defer_low ( ) each time you want use an outlet is faster, since it seems that it creates and destroys a qelem for each call. Ok, i’m not sure that it is slower either, and that’s the simplest ; but implement a clocked queue isn’t that hard.

My 2 cents.

#230250
Oct 27, 2012 at 4:49pm

Hi,

thanks for all comments. To reveal the background, my problem arises with my network externals, where the network listener runs on a separate thread, and until now, I was using that thread to do the outlet calls.

Since I expect to get every single byte (and in the order of their arrival) on the outlets, a simple qelem will not be a solution. However, I think Nicolas is right with the point that deferlowing everything might be a waste of resources.

So, my guess is that the best would be, if I kept a separate structure (probably a FIFO) with the data to output and use a qelem to trigger output. This way, I can make sure that (1) every data gets to the outlet and in the right order, but (2) I won’t flood the main thread with events, since if things get too slow, only the last qelem that has been set will have a triggering effect.

Thanks for helping me in figuring this out.

Cheers,
Ádám

#230251
Oct 28, 2012 at 10:00pm

Hi,

for those interested, I made a generic class for outlet management based on the comments above. Here it is:

The header file (without the includes etc.):

typedef struct {
	e_max_atomtypes type;
	void * outlet;
	t_symbol * message;
	std::vector < t_atom > data;
} OutletData;

class OutletManager {

public:
	t_object objectStruct;
	void outletBang ( void * );
	void outletInt ( void *, const long & );
	void outletFloat ( void *, const float & );
	void outletList ( void *, const long &, t_atom * );
	void outletAnything ( void *, t_symbol *, const long &, t_atom * );
	OutletManager ( void );
	~OutletManager ( void );

protected:
	t_systhread_mutex mutex;
	void * qelem;
	std::vector < OutletData > messages;
	static void qelemTask ( OutletManager * );

};

and the implementation:

void OutletManager::outletBang ( void * outlet ) {
	OutletData newMessage;

	newMessage.type = A_NOTHING;
	newMessage.outlet = outlet;
	systhread_mutex_lock ( mutex );
	messages.push_back ( newMessage );
	systhread_mutex_unlock ( mutex );
	qelem_set ( qelem );

}

void OutletManager::outletInt ( void * outlet, const long & l ) {
	OutletData newMessage;
	t_atom atom;

	newMessage.type = A_LONG;
	newMessage.outlet = outlet;
	atom_setlong ( & atom, l );
	newMessage.data.push_back ( atom );
	systhread_mutex_lock ( mutex );
	messages.push_back ( newMessage );
	systhread_mutex_unlock ( mutex );
	qelem_set ( qelem );

}

void OutletManager::outletFloat ( void * outlet, const float & f ) {
	OutletData newMessage;
	t_atom atom;

	newMessage.type = A_FLOAT;
	newMessage.outlet = outlet;
	atom_setfloat ( & atom, f );
	newMessage.data.push_back ( atom );
	systhread_mutex_lock ( mutex );
	messages.push_back ( newMessage );
	systhread_mutex_unlock ( mutex );
	qelem_set ( qelem );

}

void OutletManager::outletList ( void * outlet, const long & argc, t_atom * argv ) {
	OutletData newMessage;

	newMessage.type = A_OBJ; // Anything but A_NOTHING, A_LONG, A_FLOAT or A_SYM
	newMessage.outlet = outlet;
	if ( argc ) {
		newMessage.data.resize ( argc );
		memcpy ( & ( * newMessage.data.begin ( ) ), argv, argc * sizeof ( t_atom ) );
	}
	systhread_mutex_lock ( mutex );
	messages.push_back ( newMessage );
	systhread_mutex_unlock ( mutex );
	qelem_set ( qelem );

}

void OutletManager::outletAnything ( void * outlet, t_symbol * msg, const long & argc, t_atom * argv ) {
	OutletData newMessage;

	newMessage.type = A_SYM;
	newMessage.outlet = outlet;
	newMessage.message = msg;
	if ( argc ) {
		newMessage.data.resize ( argc );
		memcpy ( & ( * newMessage.data.begin ( ) ), argv, argc * sizeof ( t_atom ) );
	}
	systhread_mutex_lock ( mutex );
	messages.push_back ( newMessage );
	systhread_mutex_unlock ( mutex );
	qelem_set ( qelem );

}

OutletManager::OutletManager ( void ) {

	systhread_mutex_new ( & mutex, SYSTHREAD_MUTEX_NORMAL );
	qelem = qelem_new ( ( t_object * ) this, ( method ) OutletManager::qelemTask );

}

OutletManager::~OutletManager ( void ) {

	qelem_free ( qelem );
	systhread_mutex_free ( mutex );

}

void OutletManager::qelemTask ( OutletManager * x ) {
	std::vector < OutletData > messages;
	t_atom * argv;

	systhread_mutex_lock ( x->mutex );
	messages = x->messages;
	x->messages.clear ( );
	systhread_mutex_unlock ( x->mutex );
	for ( std::vector < OutletData >::iterator iterator = messages.begin ( ); iterator != messages.end ( ); ++ iterator ) {
		argv = NULL;
		if ( iterator->data.size ( ) ) {
			argv = & ( * ( iterator->data.begin ( ) ) );
		}
		switch ( iterator->type ) {
			case A_NOTHING:
				outlet_bang ( iterator->outlet );
				break;
			case A_LONG:
				outlet_int ( iterator->outlet, atom_getlong ( argv ) );
				break;
			case A_FLOAT:
				outlet_int ( iterator->outlet, atom_getfloat ( argv ) );
				break;
			case A_SYM:
				outlet_anything ( iterator->outlet, iterator->message, iterator->data.size ( ), argv );
				break;
			default:
				outlet_list ( iterator->outlet, NULL, iterator->data.size ( ), argv );
		}
	}

}

To use this, you just need to create an OutletManager field in your object structure, and when you want to schedule something to be outlet-ed, you just need to call one of the outlet* methods of that field, passing the outlet pointer and the appropriate variables.

Cheers,
Ádám

#230252
Oct 29, 2012 at 6:29am

Hello Ádám,

Thanks for sharing.

Just two questions :
- what is “t_object objectStruct” in your class ?
- i always call qelem_unset( ) before qelem_free( ). Is anybody knows if it is necessary (as it seems no one call it) ?

#230253
Oct 29, 2012 at 9:24am

Hi Nicolas,

I was not 100% sure whether I really need a t_object field at the beginning of the class or not, since I don’t really know how qelems work internally. However, since qelem_new is expecting a pointer to a t_object as its first parameter, I decided to have a t_object pointer at the beginning of my class (so that I could cast it to a t_object).
I guess you’re right with the need for qelem_unset(). Thanks for pointing that out. Is there anybody who knew whether qelem_unset() gets called by qelem_free() or not?

Thanks,
Ádám

#230254
Oct 29, 2012 at 10:36am

Hi,

- Oops ; i have not seen “this” in the qelem_new function and i understand now the problem and your solution ; thanks.
- In the simplethread.c exemple and in most of jamoma files, there is no qelem_unset call before qelem_free ; i found it only in QueueRamp.cpp. So i guess it is not very important.

Anybody ?

Ciao, Nicolas.

#230255
Oct 29, 2012 at 11:48am

Hi,

I started a new thread for this. I think this has nothing to do at this point with the OP. Also, I had one more question regarding qelem behaviour. See this post:

http://cycling74.com/forums/topic.php?id=43346 .

Cheers,
Ádám

#230256

You must be logged in to reply to this topic.