Semaphore

Dec 2, 2008
212
2
Canada
#1
Here is my current implementation of a semaphore using a function and shared memory.

Line to use with FUNCTION:

semaphore=%@TRIM[%@execstr[0,SETLOCAL & SET Hndl=%@SMOPEN[8,Semaphore%1] & SET OHndl=%@SMPEEK[%Hndl,4,4] & IFF %OHndl == 0 THEN & SET RtnH=%@SMPOKE[%Hndl,4,4,%Hndl] & DELAY /M 2 & SET OHndl=%@SMPEEK[%Hndl,4,4] & IFF %OHndl != %Hndl THEN & SET RtnC=%@SMCLOSE[%Hndl] & SET Hndl=0 & ELSE & SET RtnP=%@SMPOKE[%Hndl,0,4,%_PID] & ENDIFF & ELSE & SET PID=%@SMPEEK[%Hndl,0,4] & IFF %PID == %_PID THEN & SET RtnC=%@SMCLOSE[%OHndl] & SET RtnH=%@SMPOKE[%Hndl,4,4,0] & SET RtnP=%@SMPOKE[%Hndl,0,4,0] & ENDIFF & SET RtnC=%@SMCLOSE[%Hndl] & SET Hndl=0 & ENDIFF & ECHO %Hndl & ENDLOCAL]]


Called using:
%@semaphore[name]

Lets look at what it does:

[01] SETLOCAL &
[02] SET Hndl=%@SMOPEN[8,Semaphore%1] &
[03] SET OHndl=%@SMPEEK[%Hndl,4,4] &
[04] IFF %OHndl == 0 THEN &
[05] SET RtnH=%@SMPOKE[%Hndl,4,4,%Hndl] &
[06] DELAY /M 2 &
[07] SET OHndl=%@SMPEEK[%Hndl,4,4] &
[08] IFF %OHndl != %Hndl THEN &
[09] SET RtnC=%@SMCLOSE[%Hndl] &
[10] SET Hndl=0 &
[11] ELSE &
[12] SET RtnP=%@SMPOKE[%Hndl,0,4,%_PID] &
[13] ENDIFF &
[14] ELSE &
[15] SET PID=%@SMPEEK[%Hndl,0,4] &
[16] IFF %PID == %_PID THEN &
[17] SET RtnC=%@SMCLOSE[%OHndl] &
[18] SET RtnH=%@SMPOKE[%Hndl,4,4,0] &
[19] SET RtnP=%@SMPOKE[%Hndl,0,4,0] &
[20] ENDIFF &
[21] SET RtnC=%@SMCLOSE[%Hndl] &
[22] SET Hndl=0 &
[23] ENDIFF &
[24] ECHO %Hndl &
[25] ENDLOCAL


Line 1/25: Protect variables
Line 2: Open the shared memory for the semaphore.
Line 3: Get the Shared Memory Handle that is currently holding the semaphore.
Line 4: If it is zero, there is is no handle holding the semaphore.
Line 5: Place the current Shared Memory Handle into the semaphore.
Line 6: Wait a period of time, this may vary depending on how fast your
computer is.
See comments below for more detail on why this is done.
Line 7: Read back the Shared Memory Handle back from Shared Memory.
Line 8: Has the handle changed?
See comments below for more detail on why this is done.
Line 9: If the handle has changed, we have lost the semaphore, close the
shared memory.
Line 10: Zero the Handle for return value in line 24.
Line 12: We retained the semaphore, store the process ID in the
semaphore.
Line 15: The handle in the semaphore is not empty, some process is
holding the semaphore. Get the process ID of the process
holding the semaphore.
Line 16: Is the process holding the semaphore the current process?
Line 17: Close the Share Memory Handle used originally to hold the
semaphore
Line 18: Zero out the semaphore's Shared Memory, this is necessary
because another process could have already opened access to
the semaphore in line 2.
Line 19: Zero out the semaphore's Shared Memory.
Line 21: Zero the Handle for return value in line 24.
Line 24: Return a value to the @execstr, this is either zero or the current
handle to the semaphore's shared memory.


Comment:
I originally required semaphores for critical sections within TCC's event handlers for
monitors. With FOLDMONITOR, I was get multiple event within a very short period of time.
In fact the semaphore itself was prone to problems where multiple processes could get
to line 5 at the same time. This could mean that more than one process could hold the
semaphore. Line 6 to 8 prevents this, basically the last of these processes to get to
line 5 gets to hold the semaphore.

Usage:

I usually use the semaphores as follows:

DO WHILE %@Semaphore[Event1] == 0 (DELAY /M 1)
DO WHILE %@Semaphore[Event2] == 0 (DELAY /M 1)

IFF %@Semaphore[Event1] == 0 THEN
REM
REM Start Critical Section
REM

IFF %@Semaphore[Event2] != 0 THEN
Echo Error on Semaphore Event2
ENDIFF
ELSE
Echo Error on Semaphore Event1
ENDIFF
 
Last edited:

samintz

Scott Mintz
May 20, 2008
1,241
11
Solon, OH, USA
#2
Have you considered using my EVENT plugin?

Code:
Name:        Event64
Author:      Scott A. Mintz
Email:
Web:
Description: Wait for or signal an event

@EventCreate[[event_name]]
        create an event.  If event_name is not spec'd then it is an unnamed event
        and can only be used by this instance of 4NT.
@EventClose[event_handle]
        close the handle of the event created with @EventCreate
EventReset ev1 [ev2 ... [ev64]]
        set the specified event object(s) to the non-signaled state
EventSignal ev1 [ev2 ... [ev64]]
        set the specified event object(s) to the signaled state
EventWait [/t timeout_ms] [/A(ll)] ev1 [ev2 ... [ev64]]
        wait for Any of the specified events with an optional timeout.
        /A - wait for All of the events

Implements:  EventSignal,EventReset,EventWait,@EventClose,@EventCreate
Version:     1.2  Build 4
 
Dec 2, 2008
212
2
Canada
#4
set evnt1=%@EventCreate[MyEvnt]
set evnt2=%@EventCreate[MyEvnt]
echo %evnt1 %evnt2
1000 1001

From the example, I am under the impression that evnt1 and evnt2 should be same.
 

samintz

Scott Mintz
May 20, 2008
1,241
11
Solon, OH, USA
#5
They are artificial handles. There is an internal array that holds the results of the actual Win32 API call for creating an event handle. Each call to @EventCreate will return a unique value. But if the name of the event is the same, they will reference the same Windows event.

Why do you want to create the same event handle twice in the same process?

You can't use the value returned from @EventCreate in a process that didn't create it. In other words if you are running 2 tabs of TCC in TCMD, each TCC will have its own values for @EventCreate.

In TCC 1 if you:
set ev1=%@EventCreate[Ev1]
set ev2=%@EventCreate[Ev2]

and in TCC 2 if you:
set ev1=%@EventCreate[Ev2]
set ev2=%@EventCreate[Ev1]

The value of %EV1 in both cases will be 1000 and %EV2 will be 1001. However, the %EV1 handle in TCC1 references named event "Ev1" and %EV1 in TCC2 references named event "Ev2"
 
#7
I've been trying to follow this thread but I got lost at the very beginning. Where does the need for synchronization arise?

You mentioned FOLDERMONITOR getting many events in a short time. That doesn't seem to be a problem here. When I build my biggest plugin, FOLDERMONITOR has no problem logging 795 events in %TEMP in 18 seconds.
 
Dec 2, 2008
212
2
Canada
#8
Scott, I haven't yet figured out how to use your plugin for replacing Semaphore for guarding critical sections, maybe you could give an example?

Vefatica, I have had various occasion using TCC to have protected critical sections, actually the semaphore code I wrote has a critical section, since there is nothing to do this that I can see in TCC, I worked around it with lines 6 and 7.
 
#9
Vefatica, I have had various occasion using TCC to have protected critical sections, actually the semaphore code I wrote has a critical section, since there is nothing to do this that I can see in TCC, I worked around it with lines 6 and 7.
I don't completely understand, but that's OK. I recalled that I also had a plugin, EV.DLL (details below). Events are all handled by id (user supplied). It's an oldie but I brought it up-to-date and tested (a little) with TCCv19. It worked in my simple tests ... in one TCC, "evcreate ev1" ... in a second TCC, "evwait ev1" ... in a third TCC, "evset ev1" (or "evpulse ev1") ... the wait was satisfied in the second TCC. No doubt I did considerably more testing years ago when it was new.

With a little difficulty I also built a 64-bit version (which I can't test). If this may help, the files are here:
Code:
ftp://lucky.syr.edu/4plugins/ev.zip
ftp://lucky.syr.edu/4plugins/x64/ev64.zip
It implements the following. Below, I'm not sure what "name" is all about; I'll have to take a closer look at the code.
Code:
v:\> evhelp

EVCREATE id [id [...]] [/A id [id [...]]]  /A = auto-reset

EVDELETE id [id [...]]

EVSET  id [id [...]]

EVPULSE  id [id [...]]

EVRESET  id [id [...]]

EVWAIT  [/N name] [/T [[H:[M:]S[.nnn] | /M milliseconds | /D YYYYMMDDHHMMSS]
  [/A(ll)] id [id [...]]

EVKILL  name [name [...]]

EVLIST  [/E(vents) | /W(aits)] (default: both) [/P PID] [/I wild]

EVHELP

@EVEXIST[id] = 1 (yes),  0 (no)

@EVSTATE[id] = 1 (set),  0 (unset),  -1 (not found)

@EVTYPE[id]  = 1 (auto), 0 (manual), -1 (not found)

@EVINUSE[id] = wait count for id, or -1 (not found)

@EVOWNER[id] = owner PID, -1 (not found)

_EVCOUNT  = event count, this process

_EVCOUNTG  = event count, all processes

_EVWAITS  = wait count
 
Dec 2, 2008
212
2
Canada
#11
Hi Vefatica,

Semaphore and events are not quit the same and I am not sure it events will work, I will have to think it over.

Hopefully I can clear up the understanding part. Since each FolderMonitor event handler is fired as a separate thread it is possible for one or more thread to hit a statement (from my original code) like:

[05] SET RtnH=%@SMPOKE[%Hndl,4,4,%Hndl]

What thread gets to poke the handle into share memory? If you have two threads with different handles only one will get to store it's handle into shared memory. This would be called a critical section and you use objects like semaphore to restrict one thread entering the critical section at one time.
 
#12
It's the need for synchronization that I don't get.

Have I got this right? ...

You have more than one FOLDERMONITOR running. Each executes a command (in its own thread). The various commands (threads) access the same resource. You want to limit access to that resource to one thread at a time.
 
Dec 2, 2008
212
2
Canada
#13
Hi Vince,

Actually, it is one FolderMonitor firing multiple events where a critical section within the event handler did cause problems because of a shared resource, so I created the semaphores to protect the critical section of the event handler. Yes, it is to limit access to a shared resource to one thread at a time.

For example, the event handler creates a directory. If two events are running neck to neck, they may hit the "md" command at the same time, one works, one errors out, The event handler could always check to see if the directory exist first but this doesn't help either because if the two thread are running neck to neck, they will pass this too and both will still attempt the "md". In this case, you would put the check for the existence of the directory ("ISDIR") into a critical section protected by semaphores. First one into the critical sections creates the directory, all other don't attempt to create it and their is no possibility of a collision on the "md" command.

I hope that helps.
 
#14
I'm convinced that there is no user-imposed solution to the problem of multiple FOLDERMONITORs. I implemented ENTERCC and LEAVECC commands in a plugin; they are wrappers for EnterCriticalSection() and LeaveCriticalSection() (the real ones!). Then I did this (below), where t:\ and h:\temp are the same directory (%TEMP).
Code:
v:\> foldermonitor t:\ DELETED CREATED FOREVER `entercc & echo 1 %_foldercount %_time %_folderaction %_folderfile1 >> v:\fmtest.log & leavecc`

v:\> foldermonitor h:\temp DELETED CREATED FOREVER `entercc & echo 2 %_foldercount %_time %_folderaction %_folderfile1 >> v:\fmtest.log & leavecc`
This very nicely got rid of the problems with the two commands both accessing v:\fmtest.log.

However, in addition to executing the command, FOLDERMONITOR does other things (notably sets and unsets variables) which the user can't protect.

With those FOLDERMONITORs in place, I built the plugin (which causes many events in %TEMP). Here is an excerpt from the log file. It's clear that, sporadically, variables are missing. And the values of the ones that appear may be questionable.
Code:
1 1 13:34:21 CREATED 2274b0ce62364b0dbf539a806480717c.exec.cmd
2  13:34:21
1 2 13:34:22 DELETED 2274b0ce62364b0dbf539a806480717c.exec.cmd
2  13:34:22
1 3 13:34:22 CREATED 3233eae33a2c4bcb96ea473e144a2bb8.tmp
2  13:34:22
1 4 13:34:22 CREATED 3d714f13b2964f2880d4869a28f94b47.rsp
2  13:34:22
1 5 13:34:22 CREATED TMP000007E3636718949F16EF95
2  13:34:22
1 6 13:34:22 CREATED _CL_7c266d1dex
2 7 13:34:22 CREATED _CL_7c266d1dsy
1 7 13:34:22 CREATED _CL_7c266d1dsy
2 8 13:34:22 CREATED _CL_7c266d1dgl
1 8 13:34:22 CREATED _CL_7c266d1dgl
2 9 13:34:22 CREATED _CL_7c266d1din
1 9 13:34:22 CREATED _CL_7c266d1din
2 10 13:34:22 CREATED _CL_7c266d1ddb
1 10 13:34:22 CREATED _CL_7c266d1ddb
2  13:34:22
Perhaps Rex could be talked into protecting the entire processing of an event. As I said before, I don't think the user can do it.
 

samintz

Scott Mintz
May 20, 2008
1,241
11
Solon, OH, USA
#15
Granted my EVENT plugin isn't nearly as complex as Vince's EV plugin. However, events are essentially semaphores with a 0 or 1 value whereas a semaphore can have more than 1. You can create an event and set its state to signaled. then it would work akin to what Vince showed with a critical section.

Then something like:
Code:
set ev=%@EventCreate[MyEvent]
EventSignal %ev
foldermonitor t:\ DELETED CREATED FOREVER `EventWait %ev & echo 1 %_foldercount %_time %_folderaction %_folderfile1 >> v:\fmtest.log & EventSignal %ev`
The above should also work correctly for separate instances of TCC each running foldermonitor. However, I have not tested any of that.

Actually, now that I think about it, I'm not sure if everything waiting on an event gets triggered or only the first one. I'll need to research that.
 

samintz

Scott Mintz
May 20, 2008
1,241
11
Solon, OH, USA
#16
I was confusing events with mutexes. Events signal for all threads waiting for the specified event. So it would not work for what you are trying to do. Mutexes have the advantage over critical sections in that they can be used across processes. Vince's example (assuming he used critical sections) would only work within the same process.

In my EVENT plugin, I don't just call WaitForMultipleObjects() and sit there. I do the following so that Ctrl+C/Ctrl+Break handling works correctly. I call the wait function with a 100 ms timeout, then call tty_yield(), and wait again.

Code:
    do
    {
        ret = WaitForMultipleObjects( sEvt.nHandles, sEvt.hEvt, sEvt.fWaitAll, 100 );
        if( ret == WAIT_TIMEOUT )
        {
            if( sEvt.timeout != INFINITE )
                if( sEvt.timeout > 100 )
                    sEvt.timeout -= 100;
                else
                    return -1;  //timeout
        }
        else
            return ret - WAIT_OBJECT_0 + 1;

        tty_yield(0);    //let TCMD do something
    }while(1);
This should be easily modeled using the @win32 function. If I have some time today, I might play with that.
 
Dec 2, 2008
212
2
Canada
#17
Yes, semaphore and mutexes are both used to protect critical section, with the mutexes better than semaphores but a little more complex than semaphores.

Scott as for you code, you could always throw in a switch to define how to wait, your method or WaitForMultipleObjects(). I can understand why you would want to allow Ctrl+C/Ctrl+Break.

Where is the @win32 function?

Craig
 

samintz

Scott Mintz
May 20, 2008
1,241
11
Solon, OH, USA
#19
So I cobbled together a mutex function (I added it to the event plugin). And it appears to work.
I added @MutexCreate, @MutexClose, and MutexRelease. The EventWait command is used to wait on the mutex.
Code:
set mx=%@MutexCreate[MyMutex]
foldermonitor t:\ DELETED CREATED FOREVER `EventWait %mx & echo 1 %_foldercount %_time %_folderaction %_folderfile1 >> v:\fmtest.log & MutexRelease %mx`
...
set mx=%@MutexClose[%mx]
@MutexClose and @EventClose are the same function internally. They both end up calling the Win32 API CloseHandle(). So you can call either one to close a mutex handle.
 

Attachments

samintz

Scott Mintz
May 20, 2008
1,241
11
Solon, OH, USA
#20
Can you post the foldermonitor command that you are using? You mentioned that it is one command firing multiple events. I'm not seeing how you are getting parallel threads running that require synchronization. Based on the HELP for foldermonitor, I got the impression that there is a single thread per monitor not a new thread per event.
 
#21
This is a bit contrived, but if you want to see the conflict.

Make a SUBST; here it's
Code:
T:\: => H:\temp
Then do something that causes many events:
Code:
v:\> foldermonitor t:\ CREATED FOREVER `echo monitor1 %_folderfile1 >> v:\foldermonitor.log`

v:\> foldermonitor h:\temp CREATED FOREVER `echo monitor2 %_folderfile1 >> v:\foldermonitor.log`

v:\> do i=1 to 100 (touch /c t:\foo%i.bar & del /q t:\foo%i.bar)
2016-02-08 17:31:02.436  T:\foo1.bar
TCC: (Sys) The process cannot access the file because it is being used by another process.
 "V:\foldermonitor.log"
2016-02-08 17:31:02.436  T:\foo2.bar
TCC: (Sys) The process cannot access the file because it is being used by another process.
 "V:\foldermonitor.log"
2016-02-08 17:31:02.436  T:\foo3.bar
TCC: (Sys) The process cannot access the file because it is being used by another process.
 "V:\foldermonitor.log"
2016-02-08 17:31:02.436  T:\foo4.bar
TCC: (Sys) The process cannot access the file because it is being used by another process.
 "V:\foldermonitor.log"
2016-02-08 17:31:02.436  T:\foo5.bar
TCC: (Sys) The process cannot access the file because it is being used by another process.
 "V:\foldermonitor.log"
2016-02-08 17:31:02.452  T:\foo6.bar

and so on.
One FOLDERMONITOR tries to open the file before the other one has closed it.

Here are the last 10 entries in the LOG file:
Code:
monitor1 foo90.bar
monitor1 foo91.bar
monitor2 foo92.bar
monitor1 foo93.bar
monitor2 foo94.bar
monitor1 foo95.bar
monitor1 foo96.bar
monitor1 foo97.bar
monitor2 foo98.bar
monitor1 foo99.bar
monitor2 foo99.bar
monitor1 foo100.bar
 
#22
Here's another, this time without the file, and an excerpt from the output. Some errors are annotated.

Code:
v:\> foldermonitor t:\ DELETED CREATED FOREVER `echo monitor1 %_foldercount %_time %_folderaction %_folderfile1`

v:\> foldermonitor h:\temp DELETED CREATED FOREVER `echo monitor2 %_foldercount %_time %_folderaction %_folderfile1`

v:\> do i=1 to 100 (touch /c /q t:\foo%i.bar & del /q t:\foo%i.bar)

monitor2 17 19:44:36 CREATED foo9.bar
monitor2 18 19:44:36 DELETED foo9.bar
monitor1 18 19:44:36 DELETED foo9.bar
monitor2  19:44:36 CREATED foo10.bar            (missing count)
monitor1 19 19:44:36 CREATED foo10.bar
monitor2 20 19:44:36 DELETED foo10.bar
monitor1 21 19:44:36 CREATED foo11.bar         (monitor1 saw foo11.bar created twice)
monitor2 21 19:44:36 CREATED foo11.bar
monitor1 21 19:44:36 CREATED foo11.bar         (the second time)
monitor1 22 19:44:36 DELETED foo11.bar
monitor2 22 19:44:36 DELETED foo11.bar
monitor2 23 19:44:36 CREATED foo12.bar
monitor1 23 19:44:36 CREATED foo12.bar
monitor1 24 19:44:36 DELETED foo12.bar
monitor2 24 19:44:36 DELETED foo12.bar
monitor1 25 19:44:36 CREATED foo13.bar
monitor2 25 19:44:36 CREATED foo13.bar
monitor1 26 19:44:36 DELETED foo13.bar
monitor2 26 19:44:36 DELETED foo13.bar
monitor1 27 19:44:36 CREATED foo14.bar
monitor2 27 19:44:36 CREATED foo14.bar
monitor1  19:44:36 DELETED foo14.bar            (missing count)
monitor2 28 19:44:36 DELETED foo14.bar
TCC: (Sys) monitor1 29 19:44:36 CREATED foo15.bar
The system cannot find the file specified.
"V:\h"                                                                     (???????????)
monitor2 29 19:44:36 CREATED foo15.bar
monitor1  19:44:36 DELETED foo15.bar            (missing count)
monitor2 30 19:44:36 DELETED foo15.bar
 

samintz

Scott Mintz
May 20, 2008
1,241
11
Solon, OH, USA
#23
Following your example, I created a a 2nd TCC instance and ran one foldermonitor from each instance. I used my new mutex commands and it worked perfectly.
Code:
subst t: C:\TC19\event\synctest
echo %@MutexCreate[synctest]
foldermonitor C:\TC19\event\synctest CREATED DELETED FOREVER `EventWait 1000 & (echo monitor1 %_foldercount %_time %_folderaction %_folderfile1 >> c:\foldermonitor.log) & MutexRelease 1000`

and in the 2nd TCC instance:
echo %@MutexCreate[synctest]
foldermonitor T:\ CREATED DELETED FOREVER `EventWait 1000 & (echo monitor2 %_foldercount %_time %_folderaction %_folderfile1 >> c:\foldermonitor.log) & MutexRelease 1000`

do i=1 to 100 (touch /c /q t:\foo%i.bar & del /q t:\foo%i.bar)
Here is an excerpt from foldermonitor.log:
Code:
monitor1 1 11:49:42 CREATED foo1.bar
monitor2 1 11:49:42 CREATED foo1.bar
monitor1 2 11:49:42 DELETED foo1.bar
monitor2 2 11:49:42 DELETED foo1.bar
monitor1 3 11:49:42 CREATED foo2.bar
monitor2 3 11:49:42 CREATED foo2.bar
...
monitor1 198 11:49:42 DELETED foo99.bar
monitor2 198 11:49:42 DELETED foo99.bar
monitor1 199 11:49:42 CREATED foo100.bar
monitor2 199 11:49:42 CREATED foo100.bar
monitor1 200 11:49:42 DELETED foo100.bar
monitor2 200 11:49:42 DELETED foo100.bar
However, when I created 2 foldermonitors within the same TCC instance I got the same behavior as Vince. I also got some weird errors displayed while running the DO loop.
Code:
do i=1 to 100 (touch /c /q t:\foo%i.bar & del /q t:\foo%i.bar)
TCC: (Sys) The system cannot find the file specified.
 "C:\TC19\event\synctest\del"
I used the same mutex for both foldermonitors. When I created a duplicate mutex (same name different handle) and then set each foldermonitor to use the unique handle, I got the same behavior but different error.
Code:
o i=1 to 100 (touch /c /q t:\foo%i.bar & del /q t:\foo%i.bar)
TCC: (Sys) The system cannot find the file specified.
 "C:\TC19\event\synctest\h"

^C
The DO loop did not finish either (I never got the PROMPT back). I had to Ctrl+C out of it. The log file contains this:
Code:
monitor1 1 12:03:31 CREATED foo1.bar    <<== note the missing entry for monitor2 CREATE
monitor2 2 12:03:31 DELETED foo1.bar
monitor1 2 12:03:31 DELETED foo1.bar
monitor2 3 12:03:31 CREATED foo2.bar
monitor1 3 12:03:31 CREATED foo2.bar
...
monitor2 199 12:03:31 CREATED foo100.bar
monitor1 199 12:03:31 CREATED foo100.bar
monitor2 200 12:03:31 DELETED foo100.bar
monitor1 200 12:03:31 DELETED foo100.bar
monitor2  12:03:31
There's also a weird entry around line 303 in the log file. It looks like the PROMPT got redirected.
Code:
[37;42;1m[C:\TC19\event\synctest][33;40;1m monitor1 152 12:03:31 DELETED foo76.bar
 
Dec 2, 2008
212
2
Canada
#24
This is what I have been using to test my Semaphore code:

Code:
@echo on
@echo ^n---- Test.btm - %_folderaction Action ----^n^n >>%_logfile
:SEMAPHORE1
REM *** Semaphore code begins
DO WHILE %@Semaphore[3] == 0 (DELAY /M 1)
DO WHILE %@Semaphore[4] == 0 (DELAY /M 1)

IFF %@Semaphore[3] == 0 THEN         
   ECHO ==========================================
   Echo *********** _folderaction : %_folderaction
   Echo *********** _foldercount  : %_foldercount
   Echo *********** _foldername   : %_foldername
   Echo *********** _folderfile1  : %_folderfile1
   Echo *********** _folderfile2  : %_folderfile2
   ECHO ==========================================
ELSE
   ECHO >>>>>>>>>> Error on Semaphore 3 <<<<<<<<<<
ENDIFF

IFF %@Semaphore[4] != 0 THEN
   ECHO >>>>>>>>>> Error on Semaphore 4 <<<<<<<<<<
ENDIFF
         

GOTO EXITFINAL

_folderaction The type of change to the file or folder. The possible values are:
_foldercount  The number of times the condition has been triggered
_foldername   The name of the folder being monitored
_folderfile1  The name of the file or folder that was created/deleted/modified/renamed. If the file was renamed, folderfile1 is the old name.
_folderfile2  If a file was renamed, folderfile2 is the new name

:EXITFINAL
EXIT
And this is what I have used to fire the above event handler:

Code:
PURGELOG & FOLDERMONITOR /C & FOLDERMONITOR C:\TEMP CREATED DELETED RENAMED MODIFIED FOREVER (SET FastTrack=1 & START /INV C:\JPSoft\Startup\Test.btm & UNSET FastTrack)
The variable "FastTrack" is to fast track through my TCSTART.BTM.
I then update one of the files in C:\Temp using a text editor or touch +A C:\Temp\*.txt
This usually fires multiple separate TCC.
 

samintz

Scott Mintz
May 20, 2008
1,241
11
Solon, OH, USA
#25
When you launch test.btm in a separate process, how does it inherit the values of the foldermonitor variables? If your test.btm script just sends output to a disk file it seems to me the easiest thing is to this:
Code:
FOLDERMONITOR C:\TEMP CREATED DELETED RENAMED MODIFIED FOREVER `echo %_foldername: %_folderaction %_foldercount %folderfile1 %_folderfile2 >> c:\logfile.txt`

Or if you need more complex output logged then pass the variables on the command line to test.btm:

FOLDERMONITOR C:\TEMP CREATED DELETED RENAMED MODIFIED FOREVER `test.btm  >> c:\logfile.txt`
I did find that the straight echo seems to keep up, with the odd behavior every once in a while where the prompt seems to show up in the log file. The second option that invokes the BTM file seems much slower and was only able to log a couple of entries.
 
Dec 2, 2008
212
2
Canada
#26
I didn't even notice that because I was working on getting the semaphore to work, I guess it it inherits them from the parent at the time that it is spawned.
 
Last edited:

samintz

Scott Mintz
May 20, 2008
1,241
11
Solon, OH, USA
#28
Yes. It is due to its use of the error() API which was replaced with the TCError() API. Rex had a temporary work around in v19.0 but it no longer works in v19.10. I attached a working version.
 

Attachments