Problem with the lack of a simple Delay() or Sleep() method

That doesn’t sound right to me. For example, you could call Socket.Poll in an event handler and that Socket is going to fire its DataAvailable event immediately, right? Or if your code hits a loop boundary, other threads might be serviced and fire events.

Am I way off base here?

I had never heard of this rule[*], but it could be fair since calling Poll is explicitly asking for events. Same as DoEvents.

To be clear, it is possible to get an event while executing an event, but the semantics become fuzzy.

I guess this would be one reason to design using Event Driven principles and not ever expect an order to events.

1 Like

I never really thought of .poll as asking for events, as it wouldn’t occur to me to try to mix event driven comms with synchronised comms. It’s two very different coding patterns. I tend to think of .poll as updating the state of the underlying comms object, or ‘driving’ the comms object along. .poll is ‘explicit’ to the object it is called on. .doevents is ‘vague’ as the scope is so much wider.

This is what the documentation says (for TCPSocket.poll)
“Polls the socket manually, which allows a socket to be used synchronously.

There are lots of protocols, especially in instrumentation, which are essentially:
→ connect or fail
→ command1
← ack or fail
→ command2
← ack or fail

The only state after connect is command1, the only state after command1 is command2 and so on.
The state of the protocol is implied by the sequece. The ack is often implied by the receipt of data - A fixed number of bytes or a delimiter. It’s simple to process in one place using .poll, by looping on .bytesavailable or peeking .lookahead. The event driven alternative is, ‘less simple.’

So can we trust the documentation or not? Can we use .poll in a loop without worrying about our code branching somewhere unexpected. In my experience, we can. Subject to some ‘common sense’ like not remaining in any loop indefinitely or for too long.

If you do not populate comms object events you (effectively) will not get comms object events in any order. If you call .doevents or return from the function, well we know what that implies.

In a loop? Not guaranteed. Xojo does stuff at loop boundaries - like polling sockets - and you can’t be sure it won’t fire some event somewhere. You can disable this with a Pragma, but in the past few years it has been discovered that some framework functions can actually cause/allow events to fire. This didn’t used to be the case and it was mildly disruptive.

This is why you’ll run into a lot of “just use events already” advice: trying to shorthorn Xojo into being synchronous in all but the most tiny, trivial ways is like attempting to preserve a sand castle on the shore. It works for a while but the ocean always wins. :smile:

So I chat with Norman before making my post. I was warned people would argue it to death, so I made a very short basic statement.

Using Shell Synchronously I would not expect events, but if you’re using an Asynchronous Shell, calling Poll while inside an event could raise DataAvailable. That is what I meant.

Like I said - it becomes a game of semantics. I am not interested in playing. I just wanted to get the message about Events not automatically behaving as a semaphore across as politely as I could.

1 Like

The latest version of Xojo I have is 2020r2.1. I have used synchronous comms in RB and Xojo for many years. Literally hundreds of applications. I have a couple of apps in the field that have been reliably driving test equipment and lab instruments for years using the techniques I have been demonstrating.

Try this.

  var socket As new TCPSocket
  socket.Address="www.google.com"
  socket.port=80
  socket.Connect
  do
    //socket.poll
  loop until socket.IsConnected
  TextArea1.AddText "Connected" + EndOfLine

Unless you uncomment socket.poll, it’s an endless loop.

You can try it with a TCPSocket dropped on a Window with a populated Connected event too. Unless you call .poll, .doevents or return, the socket state is not updated.

On the other hand.

var socket As new TCPSocket
socket.Address="www.google.com"
socket.port=80

socket.Connect
do
  socket.Poll
loop until socket.IsConnected

var lineOut as string
lineOut = "GET / HTTP/1.1" + EndOfLine.Windows _
+ "host:www.google.com" + EndOfLine.Windows _
+ "connection:close" + EndOfLine.Windows

socket.write lineOut + EndOfLine.Windows

var pos As integer
var peek As string, lineIn as string
while socket.Lookahead.IndexOf(EndOfLine.Windows) <> 0
  do
    socket.Poll
    peek = socket.Lookahead
    pos = peek.IndexOf(EndOfLine.Windows)
  loop until (pos > -1)
  lineIn = socket.Read(pos, Encodings.UTF8)
  call socket.read(EndOfLine.Windows.Length)
  TextArea1.AddText lineIn + EndOfLine
wend
socket.Purge
socket.Close

Behaves exactly as I expect it to. It needs a timeout but proves a point.

I don’t write Xojo apps for general release. My UIs are only ever functional. Many of the apps I write have a narrow scope and short life, as they are produced at a minimal fixed cost to provide a service that would overwise be cost prohibitive. Being able to chew through a protocol rapidly and reliably can be the difference between getting the job or not. Of course I use event driven comms when necessary, and as I said it often is.

By all means I am open to someone proving what you are claiming. Show me how a loop can break state. I’ve tried and couldn’t manage it. If it really is an issue it should be reproducable. At the moment it looks like a vague and irrational fear. It surprises me.

1 Like

Exactly: the [*] in my rule was intented to mean “with a few exceptions”. DoEvents, ShowModal, and Poll are the ones that come to mind as breaking the rule.

@Matthew_Stevens - I tested your code and it works as written, but has two flaws:

  • First, it has a fairly bad failure mode. If Google.com fails to respond, then your code sits in a sight loop using 100% of one CPU core.
  • Second, try adding
    #Pragma DisableBackgroundTasks
    to your code and add event logging to the socket - what I see is that the events come in at different times, which could cause some very subtle bugs.

Is what you are describing similar to calling .poll within the .dataavailable event of the same object? Yes, calling .poll causes the framework to process whatever is queued for the object and .dataavailable may be re-entered with the object in it’s new state, and the old state sat on the stack ready to unwind. Makes sense when you think about it.

I would say more a question of context than semantics. It surprises me that useful techniques are being overlooked (being kind) without any clear evidence they don’t work in Xojo.

He did say

1 Like

I think using @Matthew_Stevens’ polling scheme, you wouldn’t put any code in the DataAvailable event and the compiler wouldn’t implement it, so that may be a non-issue (if you’re careful).

It’s not recommended, but if you want to do this style of syncrhonous (non-event-driven) code, you can make it a better citizen with a couple small changes:

  1. add Thread.SleepCurrent with a reasonable delay, such as 5 milliseconds, to all of your loops:
socket.Connect
do
  socket.Poll
  thread.sleepCurrent(5)
loop until socket.IsConnected

This will drop the CPU usage from 100% closer to 1%, with the only downside being an (up to) 5 millisecond delay in noticing that the socket is connected.

  1. If you want other events to happen, then run your socket code inside a thread. That allows you to keep a responsive UI, as while your thread is sleeping, other Events can be handled.

The bigger problem here as I see it is that Xojo does not have any easy way of doing await/async or promises, something that other languages offer.

For example, in JavaScript one can do this:

function resolveAfter2Seconds() {
  return new Promise((resolve) => {
    setTimeout(() => {
      resolve('resolved');
    }, 2000);
  });
}

async function asyncCall() {
  console.log('calling');
  const result = await resolveAfter2Seconds();
  console.log(result);
  // Expected output: "resolved"
}

See async function - JavaScript | MDN

which allows one to code in a linear fashion (Start A, wait for A to finish, Start B, wait for B to finish…).

You can do it in Xojo, but you have to add event handlers and also do some internal state-keeping logic (what if B happens but A has never happened).

1 Like

So, just to check my facts: you’ve been using Xojo for at least 20 years; your apps are limited in scope and distribution; your techniques work just fine in “literally hundreds of applications”; and you’re puzzled by/tired of people telling you about important aspects of the Xojo framework that are directly related to the question you’ve asked.

Why are you here, again?

It’s a place for discussion. I’ve learned from this one. It should be encouraged.

2 Likes

This particular discussion has been interesting with the exception of its originator, who seems intelligent and skilled but mostly interested in shooting down the proffered knowledge and advice.

Never in the history of mankind have the words “Calm down” actually made anyone calm down.

Folks, it really isn’t worth arguing over who knows the product better or who is the more capable developer. What does make you a better developer, is accepting other’s opinions and learning from it. The other person may not be right, but you also may not be right, and you know what?

That’s absolutely fine. Because as a community, the idea is to help each other learn and grow and if you can’t do that, why are you here?

I regularly throw working code, but feels wrong onto X / Twitter and Bluesky, ask for critique or if there is a better way. You’d be surprised how many people will respond with a varying amount of different opinions.

2 Likes

@Matthew_Stevens is not the OP, @Chris_Mower is, and he dropped out on Oct 8.

And I’ve learned more from @Matthew_Stevens’ posts than from any of the others.