What at first seemed like a harmless question seems to have kicked over the termite mound.
Still, there was a lot of good information to be gleaned, so I think it was worth it.
Just glad I didn’t mention the lack of linear code editing
What at first seemed like a harmless question seems to have kicked over the termite mound.
Nothing. But the real answer got buried, and we should expose.
I just said “mark the answer, say as much ‘whatever’ people want after”.
I use thread.sleep to allow for hardware settling times in some automated test equipment. A +10% error as shown can add significant time to the overall test, so thanks for this @Matthew_Stevens.
Would love to hear from @Christian_Schmitz with more details about DelayMBS, as the documentation tells us very little. How accurate is it? Is it safe to use in a thread, unlike DoEvents?
If the OP needs a delay of 100msec or so, and thinks the sleep is inaccurate, I dunno why he doesn’t do a loop with a
SleepCurrent() of say 10msec and check the now time in the loop each time after returning from the sleep. That would use a sleep but reduce the overhead significantly.
BTW I was reading some time ago that Windows can’t sleep a process to any better accuracy than 15msec. Whether this is still the case I know not.
Tim, as the OP all I did was pose the question as to the lack of the Sleep() function. I wasn’t saying anything bout accuracy, which I think has been lost in the postings.
Ah sorry, not the OP but in fact @Matthew_Stevens
I am content to let Xojo decide if it’s a bug or not. I found pretty much what I thought I would find. Yielding to a single threaded co-operative eventing framework incurs a bunch of variable latency. Xojo is hardly unique in that respect.
My first post contained code that meets the OPs requirement.
I can not speak for anyone else but I am not confused. I have a clear idea of when it is appropriate to yield and when it is not. I didn’t think it likely the framework could manage millisecond timing and my subsequent posts confirmed it. I also failed to canvas what I would consider a tangible benefit of yielding to the framework for such a short delay - So will carry on as I was.
Thank you for your concern. My apologies for challenging the group think.
Well, It will still as is so. There’s nothing to decide on top of something never reported.
Boom. Sometime, you just need a reality check!
When I first started using Xojo I went through the same thing you’re going through. It took some time, but I eventually came to realize I was doing things wrong, at least in the context of how Xojo works. And despite resisting it, I eventually sat down and did it the “right” way and my life is better for it. Pausing code to wait for something to happen often has unintended consequences that you might not want, or may not realize are happening. And sometimes, waiting whatever length of time you specified isn’t enough, if something gets stuck somewhere.
One of the ways I handle this now is by using Computed Properties. These are Properties like any other, except that you can tie code to their Get and Set events. That gives you a trigger mechanism. Let’s say you’re waiting for task A to complete before moving on to task B. Instead of having Task B sit and wait and constantly check on task A’s status, have Task A update a computed property when it’s finished. Then in its Set method, you can put the code that triggers Task B. In this way you guarantee things happen in the sequence you want and not until the proper conditions are met, and you eliminate all the messiness of setting up sleep mechanisms that will block other things in your app from happening while you’re waiting.
Here’s a set of them I’m using in the app I’m currently building. These are related to an external controller box, which is used as the interface between my software and a bunch of motors.
The private “m” properties hold the actual value. The Get method of
trTakeupTorque just returns whatever value is in
mTakeupTorque. So you don’t directly test
mTakeupTorque, you test
trTakeupTorque, which returns the value of
mTakeupTorque. Similarly, when you want to set
mTakeupTorque you actually set
trTakeupTorque, which invokes
trTakeupTorque’s Set method. Both the Set and Get methods give you a place to put code that should be triggered when they’re called. This is the set method for
Some functions of the external hardware I’m controlling could take a fraction of a second to complete. Some could take several seconds. Using Computed Properties I don’t need to care about that. When a property is set indicating something is done, I just react accordingly. In this case, I’m simply updating the UI to indicate a given motor’s current torque value. But I could just as easily have it immediately inform the external hardware to change the torque value for that motor. (which is what it will really do, but this is a work in progress)
Your case is quite valid in its instance. But, you don’t need specific timing to manage a motor’s torque or speed values. Such control can be totally asynchronous.
However, quite a few of us work with interactions that are very timing sensitive and serial in nature. We need to issue a command and then wait for a very specific amount of time to pass before moving to the next operation in the communication chain. Your method will simply not work in those instances. Or, if it will, I would appreciate someone providing an explicit example of how if would work.
This was just one example, and I picked a simple one.
Everything I’m doing is pretty much serial in nature: Initiate Task A, wait for it to be completed, start Task B, and so on. The difference is that instead of writing a linear sequence with built in pauses while waiting for things to finish, I’m creating a series of events that trigger one another in a cascade. In the end it’s the same result, it’s just a different way of getting there.
I have a timer that polls the controller several times per second. It dumps the values of the controller’s registers into corresponding Computed Properties in my app. So several times per second, all these Set methods are getting called. All of those update the UI as in the example above, and some of them also trigger other events, usually by setting computed properties themselves.
There may be times where I want to advance the film and take an image when it finishes. I may also want to advance a frame and NOT take an image of it. So I have another property that’s a boolean that is set according to what I expect to happen after the move. Instead of writing a method that advances the film and waits until it’s arrived before taking an image, I just set a flag indicating that an image should be taken after the film has finished moving, and I advance the film. If the move takes 1 second or 10 seconds it doesn’t matter. When the film arrives at where it’s supposed to be, which we know because in the Computed Property Set method for the film position, we’re testing the current position against the expected position, we will trigger the next event - to take an image. That is, assuming the “take an image” flag is set. If we’re scanning a color image, then we’re taking three images with different colored lights. So another property says “this is color”, and after the first picture is taken and the image buffer data has changed, we check the “this is color” property and if it’s true we change the color of the light and that in turn triggers the take an image event until we’ve filled the R, G, and B image buffer properties. That triggers the next step, which involves combining the three buffers into a color image. Upon completion this would set another computed property that would trigger the image registration code to align the frame.
All that from advancing one frame. Or, if we’re not taking an image and we’re just moving the film, we simply advanced a frame and we sit and wait for the next task and skip most of what the paragraph above describes.
You have to completely change the way you think about this but so far I’ve found that doing things the way I’m doing it now is so much more flexible and reliable than a linear sequence where I sit and wait for something to complete. My code is easier to follow and maintain, and is more flexible too.
Very thorough description. Thank you for taking the time to outline your full use case!
A computed property is an OOP construct. Nothing more. A computed property is specifically a function that is called by an operator. Functions have no innate sense of time. To impose a delay with a computed property, you still need to find a way to impose the delay.
Perry’s computed properties are just an interesting way of organizing the flags controlling his state machine. Timing (delay) comes from timers, hardware events, thread completion, etc. The application idles (with its main loop un-blocked) until an event happens.
Yes, I can see what Perry is doing thanks. I was answering Tim’s question. Computed properties have nothing much to do with inserting a very specific delay between two operations. Neither can computed properties make yielding to the framework any more precise than it is. Because computed properties are just functions by another name.
I’m a little confused by the controversy here to be honest. I was introduced to OOP back in 1992, porting C code to C++ for a digital telephony switch. After that I went through half a dozen eventing frameworks before I got to Real Basic and Xojo. This is the first time I have seen a community using words like ‘wrong’ and ‘not designed’ to describe looping a CPU to count a few milliseconds.
Coop eventing frameworks (like Xojo) can simplify UI and other asynchrous event handling. Variable latency is generally the price you pay for it. Concurrency is another issue to be aware of. If you yield, is there any guarantee the next line (of your) code to execute will be the line following the yield? Having an event handler called while waiting in a computed property, I would not want that to be even a remote possibility.
A few thoughts.
First, things have changed since the 1990s. You might reasonably expect your code to be the only code running on the machine, especially in a dedicated telephony system, and thus could anticipate a high degree of timing accuracy during loops, etc. That just isn’t the case in modern systems. Your code should expect to be interrupted at the most inopportune times unless you are running on a realtime operating system.
Secondly, the idea of “wrongness” is a common feeling amongst developers. All frameworks are products of human creativity, and they generally imply a “proper” way in which they are to be used. For example, the order in which your code calls frameworks methods, or the way that the framework is easier to use in one fashion versus another; these are all design decisions on the part of the framework developer and inevitably influence the code that interfaces with them.
The Xojo framework is no different. It’s not terrifically consistent in style in some areas, but the entire framework does hold to a particular vision of how it is to be used. Yes, you can use it in different ways - at the end of the day it’s just a collection of methods and data structures. But the sense of long-time users of Xojo will be that you’re “doing it wrong”. It’s not a value judgement as to the quality of your code, it’s an observation that your code isn’t behaving in a way that meshes with the intent of the framework.
And - speaking for myself here as a very long-term user of the language, 25 years and counting - this observation is based on personal experience. How many times have I gone down the direct execution path instead of taking the time to use events? Too many, and they’ve always ended in frustration and code rework. So when you hear a lot of comments like “that’s wrong” and “use events”, you’re probably on the receiving side of some hard-won wisdom.
Granted, I haven’t done much/any work with hardware that is highly sensitive to timing; but my experience with Xojo suggests that perhaps it may not be the right tool for this, just like it isn’t the ideal platform for creating most games. There are very few guarantees with the timing, order, etc. of Xojo’s events; thus they may not be a great choice for certain programming tasks.
There’s also the possibility to use a console app, which handles events differently and may be of interest in this case.
Thank you for your thoughts.
This is not the place to post a CV but you might imagine 30 years working professionally in computing may have taught me a thing or two. While nothing you said is untrue, it’s some way from the practicalities. In the 90s it took a dedicated chassis with a coprocessor to handle an E1 line. Now you can do it on a modest PC while watching a video on Youtube.
I’ve been using Real Basic / Xojo about 20 years and eventing frameworks about 30. Hopefully long enough to have an opinion. If you take a look at the Xojo framework you find poll methods with roughly equivalent functionality to event handlers. In view of the language features I would question whether the ‘vision’ and ‘intent’ was ever real, or merely consensus promulgated by repetition.
As I said earlier, I believe I have a clear idea of when it is appropriate to yield to a framework and when it is not. I am not at all doubting there are scenarios, the majority even, that will end in frustration and rework if you do not embrace framework events. Such does not discount scenarios where attempting to use events produces overcomplicated and error prone coding patterns for no tangible benefit. Horses for courses, as they say.
I guess the crux of my discomfort here is it appears people may be espousing the framework based on some quaint notion of ‘correct’ without comparable experience or testing.
If you do not yield to the framework you do not get events and execution out of order. It turns out you don’t get events with thread.SleepCurrent either. Whether timing is up to the task is application and platform specific. Looping on micrososeconds produces at least the delay you expect, and by disabling background tasks, with the least achievable overrun. However, if you want to make the timing +/- orders of magnitude worse in all cases, sure, call the framework.
The rule[*] for Xojo is that when an event handler A has started, no other event handlers (B, C, D…) will fire until A has exited.
Thus, if you call Thread.SleepCurrent() in the main thread, your statement that you don’t get other events is correct.
However, in a thread that is not the main thread, if you call Thread.SleepCurrent() or Thread.Sleep() you can receive events on the main thread while your thread is sleeping.
- [*] one exception to this rule is in modal dialogs, where a second event handler can run while the first one is ‘paused’