Recent

Author Topic: Simple FpFork example  (Read 488 times)

richardcrossley

  • Newbie
  • Posts: 4
Simple FpFork example
« on: July 07, 2020, 11:35:51 am »
Hi,

I'm new to Lazarus and I am trying to write a simple example of a GUI that can create a new instance of itself. A search suggested using the FpFork function...

https://www.freepascal.org/docs-html/rtl/baseunix/fpfork.html

I coded a simple example, as below, but my program throws an exception, that suggests I need to to enable multi-threading, which isn't what I am trying to achieve, I really do want a separate instance of the application.

Thanks for any assistance you can provide.

Terminal output

Code: [Select]
Parent process.
Start child process.
fork_test: Fatal IO error 11 (Resource temporarily unavailable) on X server :1.
[xcb] Unknown sequence number while processing queue
[xcb] Most likely this is a multi-threaded client and XInitThreads has not been called
[xcb] Aborting, sorry about that.
fork_test: ../../src/xcb_io.c:260: poll_for_event: Assertion `!xcb_xlib_threads_sequence_lost' failed.
Aborted (core dumped)


Application code

Code: Pascal  [Select][+][-]
  1. unit frm_main;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   baseunix,
  9.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     btn_fork: TButton;
  17.     procedure btn_forkClick(Sender: TObject);
  18.   private
  19.  
  20.   public
  21.  
  22.   end;
  23.  
  24. var
  25.   Form1: TForm1;
  26.  
  27. implementation
  28.  
  29. {$R *.lfm}
  30.  
  31. { TForm1 }
  32.  
  33. procedure TForm1.btn_forkClick(Sender: TObject);
  34. var
  35.   pid: TPid;
  36.  
  37. begin
  38.   pid := FpFork ;
  39.  
  40.   if pid = 0 then
  41.   begin
  42.     writeln('Start child process.');
  43.   end
  44.   else
  45.   begin
  46.     writeln('Parent process.');
  47.   end;
  48. end;
  49.  
  50. end.
  51.  
  52.  

Code: [Select]
$ fpc -version
[0.004] Free Pascal Compiler version 3.0.4+dfsg-23 [2019/11/25] for x86_64
[0.004] Copyright (c) 1993-2017 by Florian Klaempfl and others
[0.004] error: No source file name in command line
[0.004] error: Compilation aborted
Error: /usr/bin/ppcx64 returned an error exitcode

Lazarus version 2.0.6+dfsq-3
OS is Ubuntu 20.04 64 bit
Linux Kernel: 5.4.0-40-generic



marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 8555
  • FPC developer.
Re: Simple FpFork example
« Reply #1 on: July 07, 2020, 11:41:11 am »
That response is not by Free Pascal, but by the X library.

richardcrossley

  • Newbie
  • Posts: 4
Re: Simple FpFork example
« Reply #2 on: July 07, 2020, 11:49:28 am »
Is there some way to work around this, I'm using Cinnamon on Ubuntu 20.04?

Warfley

  • Sr. Member
  • ****
  • Posts: 259
Re: Simple FpFork example
« Reply #3 on: July 07, 2020, 02:19:40 pm »
This is a wild guess, but fork simply copies the open filedescriptors, which includes the xserver connection. So now you have two processes communicating with the X-Server via the same socket, which the X-Server does not like.

Your error message already tells you that this can be fixed by enabling multithreaded support in the xserver via XInitThreads, but I think most probably the best way to go about this is rather than copying your application you want to start it from ground up, e.g. using TProcess, and use different mechanisms to restore the state

marcov

  • Global Moderator
  • Hero Member
  • *****
  • Posts: 8555
  • FPC developer.
Re: Simple FpFork example
« Reply #4 on: July 07, 2020, 02:22:16 pm »
fork is a complicated call that essentially simply duplicates the process record in the process table.

However, there are a lot of rules about fork what you can and can't do, and how to treat and divide assets. (like file handles, memory etc). It is not exactly an easy call to master, especially not in a very complicated (GUI) application.

So basically there are two ways I can answer this:
  • Start reading *nix manpages and documentation to get deeper knowledge into fork
  • What are you exactly trying to achieve with fork ?

(p.s. oops, some of this are for other fork kinds like rfork and clone)

richardcrossley

  • Newbie
  • Posts: 4
Re: Simple FpFork example
« Reply #5 on: July 07, 2020, 03:50:31 pm »
TProcess, I had started looking at that, thanks for the confirmation.

I managed to find an example, that did most of what I wanted...
https://wiki.freepascal.org/Executing_External_Programs#Run_detached_program

I also managed to find some documentation on the Application variable. I was wondering if there was a TProcess instance within it which I could plunder. However most of the details I need are exposed as properties.

Many thanks for the help.

Code: Pascal  [Select][+][-]
  1. unit frm_main;
  2.  
  3. {$mode objfpc}{$H+}
  4.  
  5. interface
  6.  
  7. uses
  8.   Process,
  9.   Classes, SysUtils, Forms, Controls, Graphics, Dialogs, StdCtrls;
  10.  
  11. type
  12.  
  13.   { TForm1 }
  14.  
  15.   TForm1 = class(TForm)
  16.     btn_process: TButton;
  17.     procedure btn_processClick(Sender: TObject);
  18.   private
  19.  
  20.   public
  21.  
  22.   end;
  23.  
  24. var
  25.   Form1: TForm1;
  26.  
  27. implementation
  28.  
  29. {$R *.lfm}
  30.  
  31. { TForm1 }
  32.  
  33. procedure TForm1.btn_processClick(Sender: TObject);
  34. var
  35.   new_process: TProcess;
  36.  
  37.   env_count: integer;
  38.  
  39. begin
  40.  
  41.   new_process := TProcess.Create(nil);
  42.   try
  43.     new_process.InheritHandles := False;
  44.     new_process.Options := [];
  45.     new_process.ShowWindow := swoShow;
  46.  
  47.     // Copy default environment variables including DISPLAY variable for GUI application to work
  48.     for env_count := 1 to GetEnvironmentVariableCount do
  49.       new_process.Environment.Add(GetEnvironmentString(env_count));
  50.  
  51.     new_process.Executable := Application.ExeName;
  52.     new_process.Execute;
  53.   finally
  54.     new_process.Free;
  55.   end;
  56.  
  57. end;
  58.  
  59. end.
  60.  




Warfley

  • Sr. Member
  • ****
  • Posts: 259
Re: Simple FpFork example
« Reply #6 on: July 08, 2020, 02:05:05 am »
Quote
new_process.InheritHandles := False;
new_process.Options := [];
new_process.ShowWindow := swoShow;
for env_count := 1 to GetEnvironmentVariableCount do
  new_process.Environment.Add(GetEnvironmentString(env_count));
Are these lines really required? Because in my oppinion they shouldn't.
ShowWindow is afaik windows only. Linux does not have a concept of GUI applications therefore there is no point in such an option.
Options should be initialized empty
About InheritHandles, while execve or posix_spawn (don't know what TProcess internally uses, I hope it's posix_spawn, but for this doesn't matter) does not close all filedescriptors, unless you are forkchaining your application (that means that every process spawns a new child process before it dies) you should not run into any problems if you don't do this (that said, if you forkchain, not doing this will fire back hard). Lastly the Environment variables, if you don't specify any environment variables, it should inherit the ones from the parent. Only if you want to modify them you need to copy them.

For example:
Code: Pascal  [Select][+][-]
  1. var
  2.   p: TProcess;
  3. begin
  4.   p:= TProcess.Create(nil);
  5.   p.Executable:='/usr/bin/env';
  6.   p.Execute;
  7. end.
Prints all environment variables of the parent process. But adding a "p.Environment.Values['foo'] := 'bar';" before the execute line, will result in having only that single environment variable set

So your code reduces to:
Code: Pascal  [Select][+][-]
  1. with TProcess.Create do
  2. try
  3.   ExecName := ParamStr(0); // a more portable way, you might not always have an application object available
  4.   Execute;
  5. finally
  6.   Free;
  7. end;

richardcrossley

  • Newbie
  • Posts: 4
Re: Simple FpFork example
« Reply #7 on: July 08, 2020, 04:33:28 am »
Thanks for the information, the test application works well after removing the unnecessary lines. Much appreciated.

 

TinyPortal © 2005-2018