cesanta / mongoose

Embedded Web Server

Home Page:https://mongoose.ws

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Unclear how to send mg_wakeup from a thread that is not associated with a specific connection.

jameshilliard opened this issue · comments

  • My goal is: Call mg_wakeup from a thread which is not created within the event handler to broadcast a message to all websocket clients.
  • My actions were: Tried to figure out how to call mg_wakeup from a thread which was not created by the mongoose event handler.
  • My expectation was: It should be possible to broadcast a message to all websocket clients from a thread not created within the mongoose event handler.
  • The result I saw: Unable to call mg_wakeup from an external thread as mg_wakeup requires a connection id to be passed.
  • My question is: How do I call mg_wakeup from a thread to broadcast a message to all websocket clients when the isn't associated with a specific connection id?

Looking at the broadcast example there seems to be an assumption that the thread which calls mg_wakeup will always be associated with a specific connection id as the example thread gets created within the manager event handler.

When broadcasting to websocket clients the thread sending the mg_wakeup in many cases will not be associated with a specific connection id as it may not have been created within the mongoose event handler.

In general when broadcasting a message to clients it would presumably be quite common that the source of the message is not connection specific. This worked previously with mg_mkpipe but no longer seems possible.

Environment

  • mongoose version: 7.14
  • Target RTOS/OS (if applicable): Linux

I think you haven't seen our docs. Did you ? (sorry if you did)
Please see our documentation, and follow the guidelines in our tutorials.
IIUC, there is an example doing what you want, and it is linked and explained in this tutorial https://mongoose.ws/documentation/tutorials/core/multi-threaded/#the-one-to-many-case

IIUC, there is an example doing what you want, and it is linked and explained in this tutorial https://mongoose.ws/documentation/tutorials/core/multi-threaded/#the-one-to-many-case

Is it guaranteed that you only get one event matching ev == MG_EV_OPEN && c->is_listening per manager? From the looks of it if you say have multiple listeners on the same manager you would potentially end up spawning too many child threads with that approach.

Is there a reason that mg_wakeup requires a specific connection id rather than just a manager?

fn is the listener event handler.
Every listener will be called only once when that condition is true. That condition is only true when the listener is started.
If you have many listeners, and don't want to use one thread per each listener, then your scenario is no longer "one-to-many" from the listener perspective. A listener handles one port, if you have many listeners, then you have many ports, hence many different protocols, from our perspective to keep things simple and oriented to the vast majority of our embedded users.

The reasons for designing mg_wakeup that way, escape me, we can knock the designer and see if he comes in (please use the contract channel if you are a paying customer).

If you have many listeners, and don't want to use one thread per each listener, then your scenario is no longer "one-to-many" from the listener perspective.

The example is a bit confusing since the websocket broadcast loops through all connections in the manager rather than looping through connections specific to the listener.

if you have many listeners, then you have many ports, hence many different protocols

This isn't entirely accurate as one could listen on multiple ports with the same protocol(or similar protocols like http and https that use the same event handler), many of the examples use multiple listeners with the same event handler as well so one broadcast thread per listener would generate multiple broadcast threads AFAIU which would generally not be what's desired.

from our perspective to keep things simple and oriented to the vast majority of our embedded users

It seems to me that it would be less error prone and simpler to have a manager specific thread calling mg_wakeup rather than a listener specific thread.

Listening on both a http and https port using the same event handler function I think is likely to be a fairly common use case.

please use the contract channel if you are a paying customer

I am using mongoose with an open source project.

I beg to differ on your appreciation of the example. It loops over all connections because it has to loop over all connections to find those of interests. There is no way to know "specific connections to the listener" without looping through all connections.
No one forces you to tag connections the same way in different listeners.

I beg to also differ on your appreciation to what I said. It is accurate and correct in the realm that it is referring to.
If you need different actions, you either pass a different argument (c->fn_data) or write different handlers.
In the case you mention, I would write two different handlers, one starting the thread. Perhaps I would use the HTTP handler to redirect to HTTPS.
This is not what most of our corporate users ask for.
In fact, HTTP and HTTPS are "different" protocols... In such a case, I would have one thread doing application stuff, and one thread per listener (how many listeners will you have ? it is a small number). Listener threads get data from the application thread. Each listener thread handles comms according to the protocol it serves.

I beg to differ on your appreciation of the example. It loops over all connections because it has to loop over all connections to find those of interests. There is no way to know "specific connections to the listener" without looping through all connections.

Yeah, I just mean that the API makes it tricky to do things correctly in some cases.

In fact, HTTP and HTTPS are "different" protocols...

Technically they are...but that doesn't normally mean they need different event handlers.

I think we can simplify broadcasting a bit with a simple tweak like #2788.

I now see why you asked for a design decision.
I thought you have read the whole tutorial, and you apparently did not, as the first part clearly shows why the connection id is needed. https://mongoose.ws/documentation/tutorials/core/multi-threaded/
The "many-to-one" is an adaptation of the scheme for specific cases like yours. The main reason for the existence of mg_wakeup() is the "one-to-one" case of handling requests, which is what the vast majority of our users need.

the first part clearly shows why the connection id is needed

I see that there are use cases where it's needed, I'm not disputing that.

I don't see why it should be required for a broadcast to all websocket connections(since we want to iterate through all connections in the manager for that), hence why my tweak in #2788 simply bypasses the connection id filtering if you set the connection id to 0(which was previously not allowed at all) while still allowing for filtering for a specific connection id.

I think you can have one thread and as many listeners as you want if you just pass different fn_data to each listener, then skip thread creation based on c->fn_data.
The one-to-many broadcast will work as you tag all incoming connections the same way. You scan, you match, you send. All of them belong to the same context.

I think you can have one thread and as many listeners as you want if you just pass different fn_data to each listener, then skip thread creation based on c->fn_data.

I suppose that could technically work..but it just seems super ugly to me while creating a lot of unnecessary complexity vs making the connection param optional.

The one-to-many broadcast will work as you tag all incoming connections the same way. You scan, you match, you send. All of them belong to the same context.

I'm not sure what's wrong with just having an option to conditionally disable the connection filtering in the wufn event handler.

I mean this loop is very similar to this one, just the filtering is different.