Can TThread.Synchronize() be used in a non-graphical program (i.e. invoked from a unix shell and interacting with the user by typed commands) or does it rely on some LCL magic in the main thread which can't easily be replicated?
Mailing lists / lazarus / 2013-12
Using Lazarus for non-graphical apps
21 messages · 6 people · –
- MMichael Schnell 8
- MMark Morgan Lloyd 6
- MMichael Van Canneyt 4
- MMarc Santhoff 1
- PPaul Breneman 1
- SSven Barth 1
Reply by email Subscribe to this list Your subscription
Read-only archive. Use j and k to move between messages.
Mark Morgan Lloyd wroteCan TThread.Synchronize() be used in a non-graphical program (i.e. invoked from a unix shell and interacting with the user by typed commands) or does it rely on some LCL magic in the main thread which can't easily be replicated?
Synchronize relies on checksynchronize being called regularly from the main thread.
If you use readln() or so, then it's not useful.
Michael.
Michael Van Canneyt wrote 11 lines
Thanks, I'm reading stdin directly with a tight timeout using fpselect so it sounds like I should be able to manage something.
Mark Morgan Lloyd wrote 16 lines
There is also the event Classes.WakeMainThread (of type TNotifyEvent) which is executed if a thread calls Synchronize (and "Queue" in 2.7.1). This event can be used to trigger the execution of CheckSynchronize. Please note that WakeMainThread is executed in context of the calling thread while CheckSynchronize *must* be called from the main thread. Since you are using fpselect you could use a pipe which is filled by WakeMainThread and wait on that as well.
Regards,
Sven
Thanks, I'm reading stdin directly with a tight timeout using fpselect so it sounds like…
Mark Morgan Lloyd wroteThanks, I'm reading stdin directly with a tight timeout using fpselect so it sounds like I should be able to manage something.
This of course requests for a multithreaded application. (i.e. the threads that handle the blocking pipes communicate with the main thread via "TThread.Queue" or "Application.QueueAsyncCall").
In fact without a GUI binding currently you even can't use TTimer.
Exact,y this is why I am planing to do a "active NoGui" "Widget Type" - and since not that long I do know how this can be done. :-)
As Michael and Sven state, "TThread.Queue", "Application.QueueAsyncCall" depend on checksynchronize() (and TTimer needs a dedicated implementation in a new WidgetType, anyway).
-Michael
Michael Schnell wrote 17 lines
Thanks, yes. In practice there will be limited communication between backend threads and the frontend console, I think I've managed to organise things so that there's effectively a single point of contact and the program will be able to run in text mode (i.e. inside something like GNU Screen) or with a GUI.
The non-GUI version is a <1Mb binary smartlinked and stripped, which is credible for publishing. Haven't a clue what the GUI version will come to yet, since I want to embed symbolic debugging and a simulated logic analyser which implies pulling in a lot of code.
This of course requests for a multithreaded application. (i.e. the threads that handle…
Michael Schnell wroteIn fact without a GUI binding currently you even can't use TTimer.
Graeme G. once posted the source of or a link to a unit called "ThreadTimer", in which there is TfpTimer. That one is independant from LCL.
I don't know if the unit is part of fpc, though.
HTH,
Marc
Marc Santhoff wrote 8 lines
Yes it is.
Michael.
Graeme G. once posted the source of or a link to a unit called "ThreadTimer", in which…
Marc Santhoff wroteGraeme G. once posted the source of or a link to a unit called "ThreadTimer", in which there is TfpTimer. That one is independant from LCL.
Unfortunately it only helps if thread communication (e.g. by TThread.Queue) is used to notify the main thread). Hence the problem is the same (and addressable by using checksynchronize() with a decent timeout).
-Michael
Michael Schnell wrote 7 lines
I think you misunderstand TThread.Queue. It does not notify the main thread. On the contrary, CheckSynchronize needs to be called in order to let TThread.Queue do its work.
Likewise, TThreadTimer works only if CheckSynchronize is called on a regular basis in the main thread.
Michael.
Michael Van Canneyt wroteI think you misunderstand TThread.Queue. It does not notify the main thread.
Yes it does (from the view of the user), when the environment is decently crafted (in fact it does the same as TThread.synchronize only that the thread does not wait until the mainthread is finished with executing the queued procedure). In the end they call "WakeMainThread()" on that behalf.
On the contrary, CheckSynchronize needs to be called in order to let TThread.Queue do its work.
That is exactly what I meant to say. As well TThread.Queue and TThread.Synchronize need CheckSynchronize to be called. In fact CheckSynchronize needs to be called by the main thread and any "queued" or "synchronized" procedure in the end is executed by being called from withing CheckSynchronize.
(Sorry if I was unclear.)
-Michael
Michael Schnell wrote 8 lines
WakeMainThread may or may not be implemented. You should not count on that.
Michael.
Michael Van Canneyt wroteWakeMainThread may or may not be implemented. You should not count on that.
Right you are.
If you want to have "decent event oriented Pascal programming" (i.e. by using TThread.Synchronize and/or TThread.Queue for handling the issues the OP requested such as doing blocking I/O in Threads), you need to do an environment (which can be seen as a "WidgetType" in Lazarus speak) that calls CheckSynchronize in the main thread and that implements WakeMainThread to wake the main thread (idle while waiting for a timeout in CheckSynchronize) by means of the underlying OS.
TTimer can be implemented on top of that as a Graeme "ThreadTimer" or by other means (I intend to provide a very rough TTimer variant based on the CheckSynchronize timeout).
-Michael
Michael Schnell wrote 17 lines
I've started providing Lazarus source code for one of my projects: http://ctrlterm.com/ports.htm
I'd like to add some threading examples, maybe to this page: http://www.turbocontrol.com/easyfpgui.htm
So please share what you can. Thanks!
Right you are. If you want to have "decent event oriented Pascal programming" (i.e. by…
Michael Schnell wrote 9 lines
The OP, i.e. I, did not mention doing any I/O in threads. The question was entirely whether TThread.Synchronize was usable in a non-LCL app, so that a background thread (emulating a legacy CPU etc.) can notify the frontend that significant status has changed (e.g. it's halted due to an invalid opcode).
I've started providing Lazarus source code for one of my projects:…
Paul Breneman wroteSo please share what you can. Thanks!
Right now I am too busy with completely unrelated things to do the planned "active NoGui" Widget Type :-( .
I just can provide some thoughts and offer to reply to any questions if anybody wants to get on that task.
The basics are (now) rather clear to me:
- The stuff needs to be appropriately integrated in the "WidgetType" selection mechanism Lazarus provides.
- The stuff needs to be appropriately integrated in the "Project Source" mechanism Lazarus provides.
- You need to do (at least) a TApplication class and a TTimer class
- Any not GUI related stuff in TApplicartion should work as expected (.ExeName, Callback-events, QueueAsyncCall ....) should work as expected
- WakeMainThread needs to be implemented according to the OS we compile for.
- After the initialization a main loop is executed that calls CheckSynchronize with an appropriate timeout (see below)
- to do a really simple TTimer class my idea is:
- - calculate the common denominator (but rounding to an appropriate minimum aka "Granularity") of any instanciated TTimers' Time settings
- - use that value as Timeout in CheckSynchronize.
- - in the main loop, count a global timer and call TTimer Events as appropriate (if timer mod Timer[i].TimeSpec = 0, with Timer[i].TimeSpec := Timer[i].Time div Granularity)
- - The Granularity should be a property the user can set.
I did a testing program that checks the multiple means of main thread notification with multiple Widget Types.
If there is a new Widget Type I would modify the program to work with same and provide it for testing the WidgetType
-Michael
The OP, i.e. I, did not mention doing any I/O in threads. The question was entirely…
Mark Morgan Lloyd wroteThe OP, i.e. I, did not mention doing any I/O in threads.
He said: "I'm reading stdin directly with a tight timeout"
IMHO, the Pascal way of handling such task is using a thread.
-Michael
Michael Schnell wrote 7 lines
That's in the main code, not a background thread, and I have good reasons for wanting to do it like that.
The question was whether I could still use TThread.Synchronize for background threads, which do not in this particular case need a significant amount of OS interaction.
Mark Morgan Lloyd wrote 5 lines
OK. There are decent reasons in certain projects. But Generally the "Pascal way" is to do event driven programming. If you do a blocking I/O in the main thread, the project is completely idle until the I/O device unblocks it. This is not desirable as soon as anything should be done in the meantime. That is why it usually is the better way to do blocking I/O only in a thread. But if you do that, you mostly need a mechanism to notify the main thread about the proceedings in all threads. This is only decently handled by an event queue such as provided by TThread.Synchronize, TThread.Queue, CheckSynchronize, WakeMainThread, ...
Show quoted text 5 lines
It does not make much sense to use TThread.Synchronize if the main thread can block in any other place than in CheckSynchronize.
-Michael
Michael Schnell wrote 16 lines
Yes, I know. I must add in my own defense that I'm not "spinning", the timeout I referred to as "tight" is 100 mSec at which point the main thread does a quick check whether e.g. one of the emulated I/O processors (which are each in a thread) has enqueued a message that the operator needs to know about. In other words, it's "tightly specified to suit the application", where 100 mSec latency is negligible for a text program and anything faster would be overkill.
When I put a GUI frontend on it things will obviously be reorganised, taking into account that driving the GUI is again the main thread's responsibility. If I put an engineer's panel on it, I'd like the lights to update very much faster: possibly every 30 mSec.
Show quoted text 6 lines
It won't. Its responsibilities are limited to interacting with the user and displaying a limited amount of system state which is maintained by background threads (2x CPU, 4xIOP, possibly some extra threads for peripherals emulated over TCP- telnet for attached terminals etc.).
OK, so I admit that I hardcoded a memory test into the frontend code yesterday. But that's a console function which is only available when the system's halted, and would not be expected to timeshare. The same applies to some of the more invasive things that can be done from the engineer's panel and to reorganisation when a memory module or peripheral goes faulty or is reconnected: there are some things that aren't expected to multitask, and where it would not in fact be "authentic".
So this seems to be a project that can be done in a no-GUI way by "manually" calling *CheckSynchronize* and implementing *WakeMainThread*, so that the event queue can be managed and feed the queue by TThread.Synchronize and/or TThread.Queue.
Let us know when you get it working.
-Michael
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://lists.lazarus.freepascal.org/pipermail/lazarus/attachments/20140108/8e17d514/attachment.html>
