I have a new Web 2.0 App. In the App.Event.Opening I create a background thread (superclass Thread object) for handling communication to equipment (PLCs) via TCPSockets. The data received from this thread updates arrays stored in the App.properties, I also have semaphores in App.properties which control locks when the background thread or any App.methods need to write or view data within these arrays.
This all runs fine until a session requests data from an App.Method. My App.Event.UnhandledException is getting triggered with the Error As RuntimeException passing no ErrorNumber or Message. I thought maybe there was an error in the session code so I added Session.Event.UnhandledException but it still falls past to the AppEvent.UnhandledException.
My session code is not using any semaphore locks or accessing the shared arrays in the App.properties. The session simply passes values to an App.Method requesting data; the app method then creates the semaphore lock, queries the array data, unlocks the semaphore and returns the result to the session.
Am I running into the lack of session information Greg explains in Timers/Threads in Web apps? If so I’m not understanding it at all - Do I need to create a WebSessionContext first and run my new thread in that? Again the web session does not interact with my side thread created when the app starts.
Do I need to move my data access methods somewhere else other than App.Methods?
I’m pretty sure I’ve called common methods located in App.Methods before in other Web 2.0 projects without issues.
The only other issue on my mind is - within the side thread and the App.Methods that work with the data I use a hard lock semaphore (wait forever until locked). IE: app.myarraylock.signal But in the methods a Session may call, I only query the lock semaphone. IE: app.myarraylock.trysignal and if it returns True I query the data, otherwise I return to the session - try again later and let it decide when to recall the app.method again. (in both cases I use app.myarraylock.release to release the lock). I didn’t want any session hanging because the app.method could not get a semaphore lock to query data.
Any suggestions where to look or if my design is way off. Thanks
This is a LIE. Yep, looking back through projects, all my common methods were is the SESSION not the APP. And testing just a return 1 method causes faults.
Tomorrow I’m going to dig deeper into ‘Timers/Threads in Web apps’ discussion, in order to figure out how to make my semaphores work from a method within Session trying to access an array in Apps.Properties.
It’s going to largely depend on what you are intending to do with the data and how you are requesting it.
For instance, you are saying that the session is requesting data… how exactly is that implemented? I’m assuming it’s an event that’s firing, but is it a Timer? Or a WebTimer? Or a user event like a button push?
My first thought is, does it matter? My plan was to have all processing in App. - letting the methods in App. handle the locking and data access, just returning a result. So however a session can call a method in App. and retrieve a return value.
To answer your question, it could be a user event - calling from a Session.Method() or a call from within a WebTimer (assuming WebTimers are different from regular Timers as they have this webcontext inherited from WebControl?)
Sometimes my UnhandledException returns no error information but sometimes (<10%) I get a hint as the error is: The thread which Entered the CriticalSection must be the thread to Leave the CriticalSection
(hope I’m not confusing the issue with multiple things going on)
*(in App = WebApplication)*
Function TestRoot() As Boolean
If App.TestRoot() = True Then
' process result
It kind of validates what I found this morning by mistake, but I don’t understand why it works.
My original query code for updating my user session (web page) was in a container class a child of the WebPage. So instead of having that method call a method directly in App. I instead created a method in Session… The Session method was kind of stupid it was just a conduit for passing information to App, and getting the response and passing back to WebPage. That seemed to work. Since that worked, I then moved my query functions from App. and placed them in Session. Thus in Session.Methodx() I now lock my semaphore (located in App.) I query the Array (also in App.) then release the semaphore and return the value to WebPage.
If TrySignal returns false, you should not call Release. Otherwise, you will get an exception. Remember, anything a Session calls, whether defined in the session or in App, runs in the session’s thread.
I should have made that more clear. I don’t release if trysignal fails. The processing methods (with data received from the PLC I can’t lose) use app.myarraylock.signal which will stall my method until the lock is made. The query functions for the WebPage side, use app.myarraylock.trysignal and return “too busy” (no relase) if false.
The The thread which Entered the CriticalSection must be the thread to Leave the CriticalSection error is interesting to me, cause every time I lock and release a semaphore, it’s always in the same method, for as short of time as possible. I even will lock and release twice in the same method if I think a processing of data might take a while. All my access to shared resources (arrays, etc) between threads is within a semaphore.
Back to Session calls:
This makes sense. What is Session doing to help the jump back to App.methods()? A method from WebPage.methods() appears not to be able to call a function in App.methods() without going through Session first. Does that have something to do with WebPage is it’s own thread from Session?
So then I was looking at Greg’s suggestion from the older post (ref. in original post):
If mySession<>nil and mySession.value<>nil then Dim wsc as new WebSessionContext(WebSession(mySession)) // Run your code here Else The session no longer exists... don't do anything. End If
I’m assuming this code in within an App.method() in order to help get “session information” for the exchange of properties back to the WebPage? But I’m lost on what wsc is. Is that a WebFramework property or do you have to use it in “Run your code here”? Or just the act of declaring it makes the bond?
There is a Main thread that acts as a manager for all the sessions. Main calls the App events, but not the App methods.
You have Main start a background thread that interacts with your controllers and stores data into App properties/arrays.
Each Session runs as a separate thread. WebPage code runs within the Session thread. As such, it is session-aware. Any code it calls in a module or App needs to be informed of the session context if it needs to do session-specific activity. It doesn’t sound like your App code does, so I don’t see how Greg’s post you linked to applies.
Any code that wants to access the App properties should use the App semaphore to control access. That includes the background thread code.
Moving code from App to Session should make no difference. It’s all the same thread. If the App code simply returns a value, then it could be implemented anywhere and it would work the same.
I’ll add my 2 cents worth and stand to be corrected if I’m wrong:
Firstly sockets will block the entire app unless they are used asynchronously.
Second, Xojo Web is built on a Xojo Console app that is tested and optimised to manage its own Webthreads on a cooperative basis so we are not in full control of threading and can’t really see what happens when.
So I have started letting Greg handle my threads for me when possible, by simply using a socket to call localhost to share information between separate input and output requests to HandleURL. That may sound weird, but it seems to work because it works the way Xojo Web does, instead of fighting Xojo Web for a time slice and trying to stop its threads / my threads creating race conditions with app-level access.
On a similar theme, my son implemented a nice system for sending socket data from Xojo Web whereby the names of different data output methods are put into a stack to call them sequentially for invocation via introspection to drive a single subclassed socket. The asynchronous return is then handled by a case statement with reference to the current method call. This minimised blocking by allowing sequential calls to be handled in an asynchronous way. This is useful for REST requests.
I have had success using a socket subclass in a Web page event with the socket having a property containing a reference to the Web page set by the calling event on the web page setting the socket’s property to self. Then the socket’s content receiving event calls a method on the web page using that property to reference the web page. At the end of the web page’s receiving method the socket’s web page property is set to nil.
In summary, I have found success sticking to Xojo Webs architecture rather than rolling my own parallel architecture and trying to bridge the two using semaphores running in a cooperative environment over which I only have limited control.