API Question Calling clbkPanelMouseEvent not during clbkPreStep or clbkPostStep

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
For my simpit controller module I am writing, I have a method that checks a serial port for incoming data. This method is running as a separate thread. After processing any serial input, the following method may be called.

Line in question bolded:
Code:
void PanelEventOutput::handleEvent(Event ev)
{
	PanelMouseEvent toClick = userDefinitions[switches[ev.id].userDef][ev.state];
	
	OBJHANDLE hSimVessel = oapiGetVesselByName(vesselName);
	if (hSimVessel != NULL)
	{
		//vessel exists, so create the interface
		VESSEL2 *simVessel = (VESSEL2 *)oapiGetVesselInterface(hSimVessel);
		//and send the event!
		[B]simVessel->clbkPanelMouseEvent(switches[ev.id].eventId,toClick.mouseEvent,toClick.mx,toClick.my);[/B]
	}
}

Because the serial monitor is running in its own thread, the above method, and clbkPanelMouseEvent for a vessel, may be called at any time. So far this hasn't blown up or caused errors, but I may just be lucky.

Is it safe to call clbkPanelMouseEvent at any time? I was thinking about the consequences that might occur if the vessel's clbkPre(Post)Step was executing when clbkPanelMouseEvent is executed.

If it is unsafe to call clbkPanelMouseEvent at any time, I can queue events and call the vessel's clbkPanelMouseEvent within my module's clbkPreStep
 

Hielor

Defender of Truth
Donator
Beta Tester
Joined
May 30, 2008
Messages
5,580
Reaction score
2
Points
0
Would depend on the vessel. It's certainly possible that some vessel could be doing something that could result in bad behavior if they got a callback on the wrong thread at an unexpected time.

Really, I'm more concerned about the callback happening on another thread than anything--most Orbiter modules aren't written to be thread-safe, since it's not something they usually have to deal with.
 

dbeachy1

O-F Administrator
Administrator
Orbiter Contributor
Addon Developer
Donator
Beta Tester
Joined
Jan 14, 2008
Messages
9,216
Reaction score
1,562
Points
203
Location
VA
Website
alteaaerospace.com
Preferred Pronouns
he/him
Yes, it is unsafe to call clbkPanelMouseEvent from another thread -- Orbiter code and add-ons are not thread-safe and are not designed to be reentrant. While it may appear to work with some vessels, sooner or later it will crash.

If it is unsafe to call clbkPanelMouseEvent at any time, I can queue events and call the vessel's clbkPanelMouseEvent within my module's clbkPreStep

That's the approach you want. :thumbup: Don't forget to lock the queue in both the thread and in your clbkPreStep for both reads and writes (e.g., using EnterCriticalSection / LeaveCriticalSection). Otherwise your thread could try to add to the queue while the main Orbiter thread in your clbkPreStep is checking its state and/or removing an entry from it (thereby also writing to it).
 

orb

New member
News Reporter
Joined
Oct 30, 2009
Messages
14,020
Reaction score
4
Points
0
Does it work OK when simulation is paused?

The best is to keep the order in which Orbiter calls it, because some vessels may utilize that order in their routines and may be sensitive or not work as intended if it's different (i.e. schedule to call it in module's clbkPostStep):
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
All events except the pause/unpause event were ignored when the simulation was paused, so I never tested what would happen when the simulation is paused. Now that I am queuing events, I will change this.

To be clear, I pass a CRITICAL_REGION struct to InitializeCriticalSection() sometime before spawning the worker thread, then call EnterCriticalSection() with the same struct before performing work with the queue, then call LeaveCriticalSection() after work is done. Finally, clean up the struct (probably in the deconstructor) with DeleteCriticalSection(). Correct?

Thanks for the help :thumbup:
 

dbeachy1

O-F Administrator
Administrator
Orbiter Contributor
Addon Developer
Donator
Beta Tester
Joined
Jan 14, 2008
Messages
9,216
Reaction score
1,562
Points
203
Location
VA
Website
alteaaerospace.com
Preferred Pronouns
he/him
To be clear, I pass a CRITICAL_REGION struct to InitializeCriticalSection() sometime before spawning the worker thread, then call EnterCriticalSection() with the same struct before performing work with the queue, then call LeaveCriticalSection() after work is done. Finally, clean up the struct (probably in the deconstructor) with DeleteCriticalSection(). Correct?

You'll need to pass the address of the same CRITICAL_SECTION structure to CreateCriticalSection, DeleteCriticalSection, EnterCriticalSection, and LeaveCriticalSection (see here for more info). You're correct about when to create and delete the CRITICAL_SECTION object. Be sure to pass the same address (pointer to your CRITICAL_SECTION structure) to your EnterCriticalSection(...) and LeaveCriticalSection(...) calls so they will block if some other thread is still inside that critical section. It's a way to ensure that only one thread accesses a shared resource (a queue, in this case) at a time.

The queue needs to be locked for both reads and writes because the state of the queue may change between the "if (myQueueStateIsFoo)" and the actions after it if the code block that reads the queue is not wrapped in an EnterCriticalSection/LeaveCriticalSection block.
 

jedidia

shoemaker without legs
Addon Developer
Joined
Mar 19, 2008
Messages
10,861
Reaction score
2,125
Points
203
Location
between the planets
For my simpit controller module I am writing, I have a method that checks a serial port for incoming data. This method is running as a separate thread. After processing any serial input, the following method may be called.

I wonder if it wouldn't be easiest overall to let your module create a keyboard/mouse input event in windows... takes care of the event cue, seems most easy to reprogram and should be perfectly save...
 

meson800

Addon Developer
Addon Developer
Donator
Joined
Aug 6, 2011
Messages
405
Reaction score
2
Points
18
Letting Windows take care of it would work in simple cases, but would involve lots of work to be able to click every VC button.

Take the DGIV for example. My current method allows me to simulate a mouse click on any of the panel buttons, regardless of if they are current displayed on the screen or not.

Using the Windows calls, I would only be able to click visible buttons, and simulating clicks on other panels would require specific programming per vessel(as I would have to simulate control-up, control-down, etc to switch panels)

On more complex vessels, such as Space Shuttle Ultra, the programming needed to click a VC switch with Windows would be immense; using clbkPanelMouseEvent (and the corresponding VC MouseEvent) makes the code portable vessel-to-vessel.
 
Top