I also really enjoyed watching the video. It is a subject that I was really
excited about at the Summit and I appreciate the 4DMethod user group making
this possible.
In the video, Thomas talked about a new (to 4D) paradigm where all UI related
code could run on the main thread (the Application process) and background
processes (workers) could be used for potentially long-running code that
shouldn't block the main thread. This is exactly what many other environments
do and would be accomplished by opening all windows using DIALOG(*) from within
the Application process context and then communicating between threads using
CALL FORM (I really wish this was named CALL WINDOW) and CALL WORKER. I've been
mulling this over and am excited about some possibilities, but also see several
pitfalls that I don't know how to overcome with the current 4D language (at
least not without slow workarounds). I'm hoping to receive some guidance from
4D on these thoughts as well as spark discussion from everyone about these new
features.
(To be clear, Thomas did not recommend converting existing applications to this
approach. In fact, he recommended _not_ doing so. I have the luxury of having
just started a new project, so I'm really interested in how this approach might
work in real life.)
- There has been worry about not being able to access interprocess variables
from a pre-emptive worker process. If all the windows are in the same process,
I would expect that quite a few interprocess variables could be converted to
process variables. These variables could be passed to a worker process when it
needed to do some work so it had the most recent values. For example, some of
us keep a list of currently open windows for building a "Windows" menu.
Probably most of us keep this in an interprocess array right now and protect
access to it using a semaphore. If all windows (and menus) were in the same
process, this could become a process array and would not need to be protected
by a semaphore anymore.
Having said that, I still think there is a need for workers to be able to
occasionally access shared resources. I hope these features evolve so that
shared resources can be accessed by protecting with a semaphore or some other
locking mechanism. I understand that semaphores are somewhat expensive and, if
used a lot, would negate the usefulness of pre-emptive processes since the
underlying threads would be blocking so often. But I think they are still
useful when used judiciously and are really quite fast when needed (just a
small portion of a millisecond in most cases).
- The documentation says the Application process is automatically a worker
process. I thought this was interesting because it means that if all an
application's windows are in this process, we can use CALL FORM to execute code
within a window's context, but we can also use CALL WORKER to execute code in
that process, but not tied to a specific window. This "smells" important to me,
but I can't quite put my finger on why. Something to keep in mind, though.
- I think this paradigm is interesting for progress indicators. I like the idea
of showing progress right in the window with a spinner or thermometer. My
current way to show progress is to open a progress window in another process
since it is a bit tricky to update the UI of a window during the run loop. But
if a worker process is doing all the work, the progress indicator could be
shown in the window that started the work and the worker process could use CALL
FORM periodically to tell it how much work was done. However, I see two
problems I'm not sure how to overcome:
1. How can the user cancel some work that is being done? If I send a
message using CALL WORKER to cancel the work, I don't think it gets the message
until the work is done. Is it possible for a worker process to periodically
check for new "messages" while in the middle of work? Or would the window
itself have to chop up the work in some manner and only send the worker a bit
at a time? That seems much slower as the two processes would have to have back
and forth communication for each “block” of work.
2. By shifting the work to a worker process, the UI on the window stays
active. In some cases this is desirable, but in others you may not want the
user to be able to click or type anything in that window until the work is
done. Something to consider.
- In my current code, I tend to let almost every window have its own process.
One reason for this is that the window has full control over selections and
current records in every table. Even when the widows are array and variable
based, this is very helpful. It seems like things could get very complicated if
you had several windows open in the same process. For example, what if two of
the windows were list windows for the same table and a third table was editing
a record in that table? I suppose I could use named selections, but they seem
limited. For example, I don't know how to programmatically ask a named
selection to sort itself a certain way without making it the current selection
and then pushing the current selection back into the named selection. Is this
an expensive operation? I don't know as I've not tested it. Intuitively it
seems like it would be. It is certainly expensive code wise in the sense that
I'd have to remember to never accidentally use the current selection except to
do temporary work for a named selection.
Without an object oriented language, this makes me wish for a C_SELECTION type
variable where queries, sorts, etc. could all work directly on this "variable".
In any case, this issue looks like a deal breaker for this paradigm to me.
Hopefully I'm missing something?
- Somewhat related to the last point, I can imagine that arrays would often
need to be passed to and from a worker. But arrays can't be passed as
parameters and I understand that it would be unsafe to pass pointers to a
pre-emptive process. So we are left with packing the arrays into a C_Object or
Blob, sending that, and then unpacking into arrays. And often the reverse
process on the way back. Is there a better way to handle this?
- I imagine most of us have some kind of inter-process messaging code right
now. Mine makes it simple for a process or window to subscribe to certain
messages and then other processes can simply send a certain type of message and
any process that is subscribed will receive the message. I like this way of
doing it because the "sending" process doesn't have to know which, if any,
processes are subscribed. Of course, this is all done using inter-process
variables. I was thinking of how this type of messaging could be built on top
of these new features without interprocess variables. Perhaps this would work:
Have a worker process whose only job is to keep track of all other processes
and what they are subscribed to. Whenever a process subscribes (or
unsubscribes) to a message, they do so by sending this worker the needed
information. The worker tracks all of this in process (or just local?)
variables. Whenever a process needs to send a message, it actually sends it to
this worker which then figures out which other processes it needs to relay the
message to. This worker could also ensure that the same message wasn't sent
twice in a row when that option is important.
This gives me a potential pattern for how to convert some other pieces of code
that rely on inter-process variables to the new paradigm.
I’m really excited about these new features, but unsure how to tackle some real
world use cases. Again, I realize that many will only use these features on an
as-needed basis because it doesn’t make sense to try to convert an existing app
to them. But I want to understand the new paradigms in the context of beginning
a brand new app using best practices.
Thanks for any thoughts!
--
Cannon Smith
Synergy Farm Solutions Inc.
Hill Spring, AB Canada
403-626-3236
<[email protected]>
<www.synergyfarmsolutions.com>
**********************************************************************
4D Internet Users Group (4D iNUG)
FAQ: http://lists.4d.com/faqnug.html
Archive: http://lists.4d.com/archives.html
Options: http://lists.4d.com/mailman/options/4d_tech
Unsub: mailto:[email protected]
**********************************************************************