1. Igor Perisic
  2. PowerBuilder
  3. Tuesday, 13 September 2022 21:17 PM UTC

I'm currently working on multithreading for one app. Ran into some issues so I'm trying bunch of different solutions. Is it possible to create a datastore object and then pass it to shared memory? 

Currently I'm getting this error - Exception: Object passed to shared/remote object method is not a nonvisual user object. 

Seems like only non visual objects are able to be passed?

 

John Fauss Accepted Answer Pending Moderation
  1. Tuesday, 13 September 2022 22:08 PM UTC
  2. PowerBuilder
  3. # 1

Hi, Igor -

When you create a thread in PB, it is a non-visual thread, so the thread cannot interact with any visual object.

I recently published a PB Tech Article on multi-threading in PB, primarily regarding data retrieval. You may wish to review it:

   https://community.appeon.com/index.php/articles-blogs/tutorials-articles/2-powerbuilder/339-free-my-gui-multi-threading-in-powerbuilder

The article explains the do's and don'ts of multi-threading in PB. There is also a companion sample application in CodeXchange:

   https://community.appeon.com/index.php/codeexchange/powerbuilder/307-a-multi-threaded-data-retrieval-example

Best regards, John

Comment
  1. René Ullrich
  2. Wednesday, 14 September 2022 05:19 AM UTC
BTW: A thread can interact with a visual object that was created in the thread. ;-)
  1. Helpful
There are no comments made yet.
René Ullrich Accepted Answer Pending Moderation
  1. Wednesday, 14 September 2022 05:17 AM UTC
  2. PowerBuilder
  3. # 2

I use GetFullState and SetFullState to pass datastores between main process and shared objects.

Comment
  1. Igor Perisic
  2. Thursday, 15 September 2022 18:03 PM UTC
I ended up with a new problem however. So my initialize function creates 24 threads, each with their own datastore. Inside there, i am using SetFullState for the datastores corresponding to the thread its in. Sometimes it works with no issues, all 24 get initialized correctly. But there are times i'm seeing something funky with 2 or 3 datastores.



First inside my initialize function, i test the length of the blob being passed. Then I setFullState on the datastore for the corresponding thread. Once that is done, I call getFullState on the datastore once more to grab its "new" blob. The old blob and the new blob should be the same every time, but sometimes they are not. For example the old blobs length is 32968 and sometimes the new one is 30476, but it should be 32968 every time. Finally I use GetSQLSelect() to see why the blobs aren't matching up and here I get random changes, like the string is blank or random characters. Another example where the old blobs lengths is 47662 and the new one is 34148, and getSQLSelect() prints a string thats just "G". These changes are random, and they don't happen every time so I'm not sure what to do.



My code inside initialize, the child threads.



ll_blob_length = Len(This.dataObjectBlob) //checks the len of the passed datastore blob

if this.l_connect_database() then // this.l_connect_database() used to connect to Oracle database

This.do_object = create datastore

if IsValid( This.do_object ) then

This.do_object.SetFullState(this.DataObjectBlob)

if This.do_object.SetTransObject( This.SQLC_Local ) = 1 then

Result = true

end if

end if

end if

blob newBlob

this.do_object.GetFullState(newBlob)

if len(newBlob) = ll_blob_length then

print(OK)

else

print(ThreadName + ": " + len(newBlob))

print(do_object.getSQLSelect())



  1. Helpful
  1. John Fauss
  2. Thursday, 15 September 2022 18:46 PM UTC
OK - That is... unusual. If it was me, I would next do the following:

1. In the main thread, just for testing purposes, do the SetFullState/GetFullState using a newly-created DataStore and see if the blob's length is the same. It should be... IF the assumption that it should be the same is correct. If not, is it the same each time you test using identical data?

2. Pass the blob's length along with the blob to the shared object thread.

3. In the shared object's thread. Test the blob argument's length to see if it matches the length argument's value before doing you load the blob into the DataStore. Again, it should match, if the assumption is correct.

4. See what happens with a more scaled-back test... say, two shared object threads instead of 24. If no problems arise with two threads, slowly increase to see if there is some (unknown) threshold being crossed. The available memory on any machine is not infinite. Only Appeon Engineering can determine if there is some undocumented practical limit to the number of threads that can be spun up.

Hope this helps. Good luck!
  1. Helpful 1
  1. Igor Perisic
  2. Thursday, 15 September 2022 20:21 PM UTC
1. The blobs length is the same, and each time it is the same when using identical data.

2. I tried passing the blobs length. Its the same length as the length i get when checking the blob inside the child thread.

3. Yes in my case it does match.

4. Seems like 6 is the sweet spot. This feels way too low for something as simple as this right? I reached out to Appeon Engineers about my issue last week with a slightly tweaked version of my app compared to what i'm asking on this thread. Still waiting on them to analyze my issue, which is why I started this thread in hopes of finding different solutions like passing a datastore to a shared object, and that led me to Rene's answer. I hope Appeon Engineers are able to analyze the issue successfully soon.



A "band-aid" solution i've come across for my issue is to manually set the SQL statements from a .txt file with .SetSQLSelect(). This has been the best temporary fix but I need to get to the bottom of the real problem since future apps I work on might come across this problem as well.



I'm trying to make sense of the error im getting. I noticed that when I put a Sleep(this.TreadIdent) before `This.do_object = create datastore` inside initialize() those errors seem to go away. So it sleeps for each thread, thread1 sleep(1), thread2 sleep(2)... thread24 sleep(24). But this isn't an optimal solution, the program has to wait 24 seconds for threads to get initialized. I tried making sleep smaller, for example this.ThreadIdent/2 so halving the total time to 12 seconds, however the errors come back but not as frequent. Now, i'm not sure how sleep() fixes this, but it seems like its some sort of timing and memory issue maybe?



I think i have exhausted all my options for a solution, I might have to just rely on my band-aid until Appeon reaches back out to me. But if you have any other suggestions or advice i'm open to them!



Thanks for all the help!
  1. Helpful
There are no comments made yet.
  • Page :
  • 1


There are no replies made for this question yet.
However, you are not allowed to reply to this question.