Jump to content

Sonjli

Members
  • Content Count

    94
  • Joined

  • Last visited

Posts posted by Sonjli


  1. Hi,

    I need to schedule some actions when the time arrives. Something like Windows scheduler does.

    For example at 12.00am of every day I have to post a rest call on a web site, or at 2.00pm another task.

    I know it is simple to do it with a standard timer or a polling on system time, but is there a smart solution with OTL?

     

    Thanks in advance.


  2. Hi,

    I have this case where one OmniTask run some external code from a delphi library\Component that uses internally a TThread. This thread can't be accessed from outside.

    This external library have a class which listen for an incoming request and when this happens then it run a "onReceive" event. Something like a listening socket.

     

    One OmniTask "run" one and only one of these objects.

     

    Have I to pay attention when the "inside-thread" have to use write-resources of the Omnitask?

     

    For example when the "inside-thread" need to use the logger of the OmniTask?

     

    Some pseudo code:

       CreateTask(
          procedure(const task: IOmniTask)
          var
             lBroadcaster: IBroadcastManager;
             lLogger: ILogWriter;
          begin
          [...]
          task.Lock.Acquire;
          try
              lLogger.Log(TLogType.Info, 'Testing...', 'TEST');
          finally
              task.Lock.release;
          end;
    [...]
          // lBroadcaster is the object using an internal tthread
          lBroadcaster.OnReceive := procedure(ABroadcastMessage: IBroadcastMessage)
                      begin
                         task.Lock.Acquire;
                         try
                            try
                               lLogger.Log(TLogType.Info, 'Parsing...', 'TEST');
                               [...]
                            except
                               on E: Exception do
                               begin
                                  lLogger.Log(TLogType.Error, 'Exception (%s): "%s"', [E.ClassName, E.Message], 'TEST');
                               end;
                            end;
                         finally
                            task.Lock.release;
                         end;
                      end;
                   lBroadcaster.Connect('127.0.0.1', 'guest', 'guest', True);
    ).SetParameter([...])  // All my stuff injiections
    .WithLock(CreateOmniCriticalSection)
    .Run;

     

    Is the locking mechanism used in the right way?

     

    Thanks

    Eddy

     

     

     


  3. Hi guys,

    I hope this is the right topic to place my doubts.

    I use a DLL written in C and it need some work with pointers.

    I admit I am not very prepared with pointers (scholastic remembrances), so I hope my question to be clear.

     

    I have this structure:

     

       PODBALMMSG2 = ^ODBALMMSG2;
    
       ODBALMMSG2 = packed record
          alm_no: LongInt;
          atype: SmallInt;
          axis: SmallInt;
          dummy: SmallInt;
          msg_len: SmallInt;
          alm_msg: array [0 .. 63] of AnsiChar;
       end;

    And this is the function I use to populate this structure and read it:

    procedure TRdAlmMsgActionA.InternalRdAlmMsg;
    var
       iAlarm: Integer;
       LErrorCounter: Integer;
       rn: SmallInt;
       popmsg: PODBALMMSG2;
    begin
       FPath := '';
       LErrorCounter := 0;
       // A random number the DLL need to have any initial value
       rn := 50;
       try
          // A pre-filled number with "max paths" (FConnection.MachinePaths) to read from DLL 
          for var J := 1 to FConnection.MachinePaths do
          begin
             // A DLL function to loop between "Paths" until "FConnection.MachinePaths"
             FConnection.ErrHandle := cnc_setpath(FConnection.ConnHandle, J);
             if FConnection.ErrHandle <> EW_OK then
             begin
                Inc(LErrorCounter);
                Continue;
             end;
    
             // Here I need help: is this algorithm ok?
             new(popmsg);
             try
                // The DLL fill "popmsg" with an array of "PODBALMMSG2" with length "rn"
                FConnection.ErrHandle := cnc_rdalmmsg2(FConnection.ConnHandle, -1, rn, popmsg);
    
                if FConnection.ErrHandle <> EW_OK then
                begin
                   Inc(LErrorCounter);
                   Continue;
                end;
                // Loop for "popmsg" of length "rn"
                for var I := 0 to rn - 1 do
                begin
                   // Here I do reading stuff with "popmsg^.alm_no", "popmsg^.aType", etc.
                   
                   // Incremet pointer to read next array value
                   Inc(popmsg);
                end;
             finally
                // Bring the pointer at the start of the array
                Dec(popmsg, rn);
                // Free the pointer
                Dispose(popmsg);
             end;
          end;
       finally
          cnc_setpath(FConnection.ConnHandle, 0);
       end;
    end;

     

    I have so many doubts about this:

    - Have the popmsg pointer to be brought at the start before dispose?

    - Is the loop right?

    - Is the "new()" function right to allocate the pointer?

    - And so on...

     

    Any help?

     

    Thanks in advance.

    Eddy

     

     

     

     

     

     


  4. Hi,

    sorry for late reading.

    I am trying to build a  kind of a "protocol runner".

    I need an object which can accept different "actions" and then it can be run in loop to repeat the protocol.

     

    Example:

    Name: "Protocol 1: download a file with locks"

    1. Wait until "the other" lock file disappear

    2. Put "my" lock file in that folder

    3. Download the main file

    4. process the main file

    5. Delete "my" lock file

    7. Wait 5 seconds

    8. Restart from 1

     

    The entire loop must be run in a separate non-blocking thread. But every loop must be serialized.

    I thought that pipeline could be the answer, but I can't run it several times in that loop.

    I may use a state machine (https://github.com/malcolmgroves/TStateMachine) but I need to build several protocols changing one or more "actions" at runtime...

     

    I don't know if this is the right choice.

    Is my idea clear enough? I hope so... 🙂

     

    Thanks

     

     

     


  5. Hi,

    simple question: how can I run a pipeline more than one time in the same method\procedure\function?

    I do like this

    ...
    MyPipeline := Parallel
    	.Pipeline
    	.stage(myStage1)
    	.stage(myStage2);
    ...
    procedure MyProcedure;
    begin
    // First run
    MyPipeline.Run;
    MyPipeline.input.Add('ONE');
    aValue := MyPipeline.Output.Next;
    
    // I tries also this... without success
    MyPipeline.Cancel;
    MyPipeline.WaitFor(INFINITE);
    
    // I do some stuff in the middle
    
    // First run
    MyPipeline.Run; // Here I receive "Pipeline is already running", but I don't think so... :(
    MyPipeline.input.Add('TWO');
    aValue := MyPipeline.Output.Next;
    ...
    end;

    I am not finding any info in the docs.

    In the sources of IOmniPipeline I see a "opCountStopped" semaphore that is checked "Assigned" in the first run... but I really don't understand why this limits one instance of a IOmniPipeline to be run only one time in the same method.

    Thanks again

    Eddy

     

     


  6. Hi Primoz,

    yes, my example is not so clear.

    My idea is of making something like a "Guardian\watchdog" (ok, they are not the same...) service who calls an url every X seconds, and if it does not respond then it delegates the calling service to do whatever it wants (it can, for example, stop the running service and restart).

    I thought the solution was of using the "OnMessage" technique and delegates there the calling service.

    I don't know if this is the right pattern, nor the right solution, indeed.

    I hope to be more clear...

    Thanks


  7. Hi Primoz,

    It's all clear. Very clear.

    5 minutes ago, Primož Gabrijelčič said:

    The task is stopped from a different thread. ServiceCreate and ServiceStart/ServiceStop are NOT executed from the same thread.

    This is a new precious info, for me. Where did you get these infos? Only by debugging?

    ServiceCreate is in one thread, and ServiceStart/ServiceStop in second thread or every event is in a separate thread? (so are they three?)

    6 minutes ago, Primož Gabrijelčič said:

    The OtlTaskControl code expects that the task will be terminated from the same thread that it was created in

    This is the second precious info. Some of my test are closed.

     

    11 minutes ago, Primož Gabrijelčič said:

    Solution? Just do your OnTerminated processing in ServiceStop.

    This has perfectly sense

     

    Thank you very much.

     


  8. Hi (here again...),

    I have a doubt about delegation.

    I have this simple case

    TWSGuardian = class(...)
    strict private
       FOnNotResponding: TProc<Integer>;
    ...
    public
       // Delegation
       property OnNotResponding: TProc<Integer> read GetOnNotResponding write
          SetOnNotResponding;
    end;
    
    // OTL task
    ...
                   Task.Comm.Send(MSG_WSNOTRESPONDING, TOmniValue.CreateNamed(['timeout', LPassedTime]));
                end;
             end;
          end
          )
          .OnMessage(MSG_WSNOTRESPONDING,
          procedure(const Task: IOmniTaskControl; const msg: TOmniMessage)
          begin
             DoNotResponding(msg.MsgData['timeout']);
          end
          );
    ...
    
    procedure TWSGuardian.DoNotResponding(TimeOut: Integer);
    begin
       if Assigned(FOnNotResponding) then
          FOnNotResponding(TimeOut);
    end;
    
    ...

    When calling the TWSGuardian object I would use a critical section to avoid collisions in case of more than one running TWSGuardian. Like here:

    begin
       Grd := TWSGuardian.Create; 
    ...
          Grd.OnNotResponding := procedure(ATimeOut: Integer)
             begin
                if ATimeOut > 1000 then
                begin
                   // Critical section?
                   RunSomeAlienCode; // How can I be sure this is thread-safe?
                end;
    		 end;
    ...
       Grd.Run;
    ...
       Grd.Stop; // This safely terminate the task inside TWSGuardian

     

    Is this the right way? Is there a common used pattern?

    How can I move the critical section inside my TWSGuardian instead of using it in the main thread?

    I am a bit confused.

     

    Thanks and sorry for this PITA

     

     

     


  9. Hi,

    I attach a mock project with the "OnTerminated" event not firing.

    - The project is an NTService, so you have to install with "/install"

    - I write the logs (a bit verbose...) on the default Windows Event Monitor (under Windows\Application)

     

    To reproduce

    - Install

    - run the service

    - wait a punch of seconds

    - Stop the service (not pause, it is not implemented for this purpose)

    - Open Event Monitor

    - The messages linked to "OTL.NTService" Origin finish with a serie like this:

     

    Keep Alive
    
    A try to stop task...
    
    FRunner.ExitCode = 0
    
    ServiceStop

     

    You'll never find a "Server stopped in ONTERMINATED" message, that is the one fired inside the "OnTerminated" event.

     

    I hope to be clear...

     

    Thanks again.

     

    PS: I use D10.3.1 but I think the sources can be good for oldest versions.

    OmniThreadNTService.zip


  10. Hello guys,

    I am working to port a NTService with a WebBroker application (wrapping Daniele Teti's DMVC) from TThread style to OTL.

    It's almost ok (and amazing, yes!) but in the next code the "OnTerminated" event never triggers (I never see the "Server stopped" string in the log)... What am I wrong?

     

    Thank you in advance

     

          ...
    	  LServer := TIdHTTPWebBrokerBridge.Create(nil);
          FRunner := CreateTask(
             procedure(const Task: IOmniTask)
             var
                LMvc: TIdHTTPWebBrokerBridge;
                i: Integer;
             begin
                LMvc := Task.Param.ByName('mvc');
    
                RunServer(8889, LMvc);
                try
                   repeat
                      Sleep(25);
                      {$IFDEF DEBUG}
                      inc(i);
                      if (i mod 40) = 0 then
                         Task.Comm.Send(MSG_LOG, 'Keep Alive');
                      {$ENDIF}
                   until Task.Terminated;
                finally
                   StopServer(LMvc);
                end;
             end
             )
             .SetParameter('mvc', LServer)
             .OnMessage(
             procedure(const Task: IOmniTaskControl; const msg: TOmniMessage)
             begin
                if msg.MsgID = MSG_LOG then
                begin
                   Log.Debug(msg.MsgData, '');
                   // LogMessage(msg.MsgData, EVENTLOG_INFORMATION_TYPE);
                end;
             end
             )
             .OnTerminated(
             procedure(const Task: IOmniTaskControl)
             begin
                Task.Comm.Send(MSG_LOG, 'Server stopped');
                if Assigned(Task.FatalException) then
                begin
                   var ELocal := Task.DetachException;
                   Task.Comm.Send(MSG_LOG, Format('...with Errors: "%s"', [ELocal.Message]));
                end;
             end
             );
    ...
    // In the ServiceStart event
    ...
       FRunner.Run;
    ...
    // In the ServiceStop event
    ...
       FRunner.Terminate;
    ...

     


  11. 1 hour ago, Olivier EXEL. said:

    I meant to do that once only before the loop : 

    
    otlWaitGroup := CreateTaskGroup; // Here create it

    OK, but THAT is the problem... if I create the group outside the loop then memory increase; if I put that inside the loop, memory is stable.

    Anyway, I am trying with background workers. They are awesome.


  12. Thanks Olivier. I'll try the background workers.

     

    For the sentence:

    Quote

    Your use of TaskGroup is once, before ce loop ; then your "join" in the loop attach the task to the group.. 

    Sorry, but I didn't understand... (maybe my poor english? 🙂 )


  13. While experimenting I found that the problem is the creation of the otlWaitGroup.

    I created it out of the main loop; so now I moved it inside the loop like in the code below.

    I have some doubts:

    Why the reference to OmniTasks stay inside the WaitGroup until it is detroyed? Are they weak references?

    Why do I have to create many waitGroups instead of creating only one, and use it many times?

    Thanks anyone.

    	   while not GetArticoloVersato.getTabella.Eof do
                   begin
                      ...
                      ATaskList := CreateTaskControlList;
                      otlWaitGroup := CreateTaskGroup; // Here create it
                      try
                         // Run all registered threads
                         FBroadcastActions.ForEach(Action<IBroadcastAction>(
                            procedure(ABroadcastAction: IBroadcastAction)
                            begin
                               ATaskList.Add(CreateTask(
                                  procedure(const Task: IOmniTask)
                                  begin
                                     Sleep(100); // Only for test
                                     // ABroadcastAction.Run(Task, GetMacchina);
                                  end).MonitorWith(FEventMonitor).Join(otlWaitGroup).Schedule(otlPool));
                            end
                            ));
    
                         otlWaitGroup.WaitForAll;
    
                         if otlPool.CountExecuting > 0 then
                            raise Exception.Create('Oh my..., thread still running');
    
                         ATaskList.Clear;
                      finally
                         otlWaitGroup := nil; // Here free it
                         ATaskList := nil;
                      end;
    					...
                    end;

  14. Hi,

    I am trying to use your fantastic library (thanks for it).

    I have a doubt about freeing memory. I have a master thread (a standard TThread by Delphi) that is responsible to run some omni tasks.

    I use to do it in this way:

    procedure TThreadMonitorBroadcast.Execute;
    begin
    ...
    			   while not GetArticoloVersato.getTabella.Eof do
                   begin
                      ...
                      ATaskList := CreateTaskControlList;
                      try
                         // Run all registered threads
                         FBroadcastActions.ForEach(Action<IBroadcastAction>(
                            procedure(ABroadcastAction: IBroadcastAction)
                            begin
                               ATaskList.Add(CreateTask(
                                  procedure(const Task: IOmniTask)
                                  begin
                                     Sleep(100); // Only for test
                                     // ABroadcastAction.Run(Task, GetMacchina);
                                  end).MonitorWith(FEventMonitor).Join(otlWaitGroup).Schedule(otlPool));
                            end
                            ));
    
                         otlWaitGroup.WaitForAll;
    
                         if otlPool.CountExecuting > 0 then
                            raise Exception.Create('Oh my..., thread still running');
    
                         ATaskList.Clear;
                      finally
                         ATaskList := nil;
                      end;
    					...
                    end;
                    ...
    end;

    It looks like everything works finem but RAM is increasing at every loop. What am I wrong?

    I tried to use a TaskControlList, but without success....

     

    Thanks in advance

    Eddy

×