Variable with the same name, different module and procedure

I have a source procedure A and when the Main window calls it simultaneously from the same thread, e.g.:

start(a,25000,par1)

then

start(a,25000,par2)

the variables get overwritten.
Then I went to add a thread to all local variables (I don’t use global variables at all).
The problem occurs when I have 2 source procedures in two separate modules and when I declare a queue with the same name and thread attribute:

myq queue,pre(m),thread
id long
end

it gives me the error

..\test.MAP(1,1) : Error : Duplicate symbol: P1$TCB$MYQ in th003.obj, th002.obj

Build Failed.
Can someone tell me if there is another way to declare queue variables in different procedures with the same name, so that they don’t get overwritten if I call them from the same thread?
Thanks.

Your description is vague, what variables?

Par1 and Par2? Variables declared in the Procedure? In the Module? It would make sense if it was Module variables if they are not THREAD.

You say " in different Procedures " but it appears the Queue is declared at the Module level?

I think you can’t, its a bug in the Linker that it makes Public that symbol. Maybe @jslarve will confirm or have a work around as I know he reported it. Where this often happens is Queue’s or File’s declared in a Class Module.

You could try adding PRIVATE to the QUEUE, but I think that won’t work.


If your desire is to have the same code using the same Queue name a workaround could be to define the Module Queue with a unique name (to avoid the dup error). Then at the top of each Procedure declare a reference to that queue with the name you desire.

Module

MyQ_Module2 queue,pre(m),thread   !Name is Unique with "_Module2" added
id    long
    end

Each Procedure:

MyQ     &MyQ_Module2  !The name I want in Procedure code is MyQ
   CODE
   MyQ &= MyQ_Module2   !Reference to Module queue   
   LOOP X=1 TO RECORDS(MyQ)    !Really MyQ_Module2
        GET(MyQ,X)

Where do you declare these? I know that some variable end up with Module scope, Queue’s might be one of them.
A quick fix would be to generate only 1 procedure per module. I do that anyway because it’s easier to find and fix errors. Or in the procedure template just select a different module for one of the procedures to be in.

Firstly - Adding ,THREAD to local variables is meaningless. And likely a problem.

As others have pointed out though if yhese are in fact Module variables then they are indeed “global” (in every way except scope) so ,THREAD is necessary.

But if they are Module variables then you need to explain more. Are you saying you have module variables with the same name in different modules? (Dont do that, just make them Global ,THREAD instead.)

OK, there are 2 problems, and attached is a test application where you can see what I mean (related to one of the problems). These are 2 source procedures that use the same name in different modules. When I compile, I get this error (see image below).

As for the second problem, I’m not sure if the problem is in my part of the code or what.
Basically, sometimes I get a different result than expected.
That’s why I suspected that local variables were being overwritten (here I mean local (not modular) variables in the source procedure. I was convinced all my life that these variables were isolated (unlike module and global variables) and that the “thread” attribute was not needed, but I have, for example, this situation:

getprojectname routine
  some code..
  loc:var1 = 'something'
  mylogproc(loc:var1) <- I see what is written
  do finish

and then

finish routine
  mylogproc(loc:var1) <- here I get 'someOtherResult'

so, the “main” procedure calls the source procedure multiple times, maybe 10 or 20 times per second:
start(mysourceProc,25000,par1) or start(mysourceProc,25000,par2)
etc.
and his scenario above happens. It’s very difficult to replicate, I spent a lot of time on it. Also, it often it happens that in the “finish” routine I get some result, but I don’t have any record from the getprojectname routine (and I should), so I’m not sure where the problem is.

So I went to add threads to local variables and discovered the problem with the same queue name and the thread attribute.

I can’t add variables globally because each procedure has 20-100 variables, some with the same names, but different lengths or decimal places, etc.

That’s why I’ve already rewritten the application several times to not use threads, i.e. instead of start(xxx) I simply call the procedures without threads because then everything always works 100% correctly, the only problem is that other users have to wait.

here is the sample app
sw.zip (9.6 KB)
and this is what I get:

Theres no embed code in your example app procedure Main.

As Proc1 and Proc2 are source procedures you should read the help page titled:

Launching a thread - behind the scenes

TLDR
If Starting Source procedures, because these procedures dont have their own accept loop, you need to Resume the thread after Starting it.

Resume(Start(MySourceProc,35000,PassedParam1))

WRT your first post, what exactly are you trying to achieve?

If the Start is in an embed within the accept loop, it will Start and pass a variable every cycle of the Accept loop, and the Accept loop can be short stopped, in other words returned to the top of the loop for a new cycle.

Thanks for the comment, I’ll definitely stay away from threads.
The question remains of two same threaded queues in different procedures. The question is what is really happening and are local queues (in local procedures) 100% thread-safe

Threads are not difficult, just remember that global vars that are NOT threaded, and all global Queues need to be wrapped with a synch object of your choice, to ensure data corruption doesnt occur, and the above point about Resuming Started threads if the procedure doesnt have an accept loop, which all windows procedures have by default but source procedures like your example doesnt have an Accept loop.

Now I havent tried, but adding an Accept loop to a source procedure should remove the requirement to use Resume, but dont quote me on that.

The choice of synch objects can be found in the help docs.

Synchronization Objects

Whilst Critical Sections are the suggested form of locking, because it doesnt give feedback namely it doesnt state if a lock has been achieved you’ll have to have a global var which is used to control the lock.

More info here on the Critical Section and Wait Objects.

The other interesting thing I have noticed when using Critical Sections is how the OutputDebugStringA appears in debug view.

When running multiple threads with a Critical Section and using OutputDebugStringA, you’ll see one or two lines from each thread appear in debugview and then a thread switch occurs.
When running multiple threads without a CriticalSection and using OutputDebugStringA, you’ll see a much larger batch of OutputDebugStringA strings in debugview before a thread switch occurs.

Now I wonder what the overheard is switching from one thread to another when using CriticalSections, but I dont know if the thread is on one core and the other thread might be on another core, which could reduce the overhead if data is stored in the core cache. They can do some fancy premptive things using core cache.

When not using CriticalSections and seeing the batch of OutputDebugStringA output it would suggest more code from a thread can run before a thread switch occurs. Likewise CPU scheduling can also be used to alter how many OutputDebugStringA appear in debugview before a thread switch occurs. Having CPU Scheduling set for Programs (workstations) or Background Services (Servers) is what can influence the thread switching.

From a debug perspective using debugview, having CriticalSections switched off can be easier to follow and thus debug before switching critical sections on, but debugview does colorise threads if the thread number is included in the OutputDebugStringA string, which can make it easier to follow in debugview.

Local Queues are thread safe if defined in the procedure. Not sure on the module or the routine because I remember Alexey saying there was a compiler switch/pragma which could alter the scope of routine declared variables. ***

Module declared queue’s I’ve never used, but should be thread safe as these are contained within a thread.

Its OS thread switching which potentially** causes the problem with unthreaded variables and queues.

**I say potentially because I would imagine Microsoft tries to avoid problems with apps using true threads and no global var locking synch objects.

*** Found the post I was thinking of.
Search for: Implicit Variables - Big Mistake or Bad Idea?

The problem is that you’ve defined your local vars as ,THREAD which effectively makes them static, which in turn allocates them on the [heap], which more or less makes them global (in definition if not scope), and there’s 2 of them. Hence the duplicated message.
If you remove the ,THREAD attribute you will have what you need. The vars will be local to that procedure only, and there are no compile errors.

[Bruce edited “stack” to “heap”. Which I think is what Sean meant.]

I suspect your app was open in the IDE when you zipped it. Hence the app itself seems to be missing all your code. Always close the app file in the IDE before zipping.

Feel free to rezip and repost. As noted earlier , the ,THREAD attribute on local variables is just plain wrong. So You can remove that. But that leaves your original question to answer, and an example is necessary to comment there.

Basically, there are 2 source procedures and each one contains just this (in the data section):

myq             queue,pre(m),thread
id                long
                end

that’s all, no other code, just data.
When you try to compile it, you’ll see this:

Error: Duplicate symbol: P1$TCB$MYQ in test003.obj, test002.obj

But as @seanh said, adding threads is the reason for the compile error and I knew that,
I just wanted to make sure that the local queues are 100% thread safe because of the unexpected values ​​I had.
However, when you declare 2 of the same global queues, it will just warn you about duplicates, but you can compile that.
Looks like I have to look elsewhere to solve my problem, thanks everyone anyway.

I would agree that this should be the situation

I wonder if it is possible that maybe the myLogProc is the problem?

Also what version of Clarion? I know that the IMDD had thread issue to do with queue’s that were sorted out in the most recent version. I’m wondering if something similar.
You might also want to log a bit sooner as well to make sure of what the var is. Also ensure that there is no possibility of alteration anywhere unexpected.

PTSS# 39274 is almost 13 years old :slight_smile:

yes, I wondered the same but it is other procedure and it have also only local variables so it should not be the problem. to be 100% sure, I also commented that procedure for testing purpose.
I’m using v11.1.13855 and no IMDD here.
Like said, it can happen the I see log twice (inside “finish” routine) without parent record from inside getprojectname routine so I presume it is some general problem there and not related to threads (but I will try to stay away from them anyway).

omg, I saw that now.