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.
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.
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.
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.
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.
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:
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.
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"
}
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.
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.