Ask Reuben – September 9, 2025

Starting a Program Only Once

How can I start a program only once? 

How can I allow a user to only start one instance of a program?

The question of how to limit a program so that is only started once is a question I see get asked a few times.  It is an odd one as when I was a junior Informix-4gl developer in the mid 90’s, we had a mechanism that did this, and that technique still works today.  There are a lot of variables to factor into a potential solution and so whilst there is a Genero method available, it might not suit all scenarios.

First of all I will give some reasons why you might not see the solution you want as built-in Genero functionality

  • The licensing mechanism does not keep track of what program is being run.  If you look at the output of flmprg -a info users you will see that there is only a process id, there is nothing that indicates the command used, program name or command line arguments.
  • fglrun is not necessarily the command used to run a Genero application.  Whilst you no longer need to create a runner which meant that the command used to run a Genero application could be anything, some customers as part of their Genero transformation would create symbolic links to fglrun so that their command to start an application remained the same.
  • you can use unique command line arguments to give a Genero application different behaviour, so it might be that you want to limit the execution when the program has a certain set of command line arguments.
  • in a multiple server scenario, you have to consider that the program you want to limit is on one server instance and an attempt may be made to start the same program on a different server.
  • is the idea to limit the program to just being run one at a time by all users, or to limit the program being run once per user or some other criteria?  For instance a stocktake update program might have a requirement that is run only once per warehouse.  You can run multiple stock update programs at a time but only one per warehouse.
  • when a program is launched via RUN WITHOUT WAITING in Genero, their is no mechanism for the child process to tell the parent process it has finished.  In fact the parent process could have terminated before the child process finishes.  StartMenu is also starting programs without waiting.
  • need to consider the case when the Genero application unexpectedly stops, scenarios such as wether the runtime crashes or the user closes a browser tab, or turns off their PC whilst a program is running.  Need to provide a mechanism that recognises this might occur and to provide a mechanism to recover.

The requirement to limit the number of times a process can be run is a valid business requirement.  Examples you will typically encounter is programs that do some form of month-end or year-end processing.    You only ever want one instance of this particular program to be running at any one particular point of time.  You might want to limit the number of programs by some criteria, I mentioned above the stocktake update scenario where it might be that you want to allow each warehouse to have at most one instance each of a program running to update their stock quantities.  You might have a program that is resource intensive so you only allow each user to run one instance of a program to prevent the system being overloaded.

So how can you code such a solution.   If you look around the real world, you will see plenty of real life scenarios where there is a limit.  Token (railway signalling), Air Traffic Control Flight Progress Strips are some interesting ones that use a physical mechanism or token.

The basic technique to employ is to …

  • at program or functionality start:
    • check for a token that indicates that a program is in use
    • if that token does exist then
      • exit the program with an appropriate error message
    • else
      • create a token and carry on running the program
  • at program or functionality end:
    • remove that token that indicates that a program is in use
  • implement a recovery mechanism to cater for the case where a program ends unexpectedly:
    • when the token is created include information that allows you to unique identify what program is running
    • provide a mechanism so that you can if the program is still running
      • if it is not running, remove the token
  • protect that token from being changed unintentionally.

Ideally the check and create at the beginning are a single step to protect against two processes running at almost the same time and the steps being A-check, B-check, A-write, B-write.  You need to protect against that scenario and have B-write error or combine the check and write so that they are performed as one step.

What is that token , typically there are two forms this will take, one is to create a file, the second is to have a database table.

For the solution using a file, the above then becomes …

  • at program or functionality start:
    • write a file that indicates that a program is in use
    • if that file cannot be written i.e it already exists
      • exit the program with an appropriate error message
    • else
      • carry on running the program
  • at program or functionality end:
    • delete that file that indicates that a program is in use
  • implement a recovery mechanism to cater for the case where a program ends unexpectedly:
    • when the file is created include information that allows you to unique identify what program is running
    • provide a mechanism so that you can if the program is still running
      • if it is not running, remove the file
  • protect that file from being changed unintentionally.

… the key things are to put some information in that file, that might be the process identifier, the name of the program etc.  This then allows you to implement a recovery mechanism by checking if that process identifier is in use and/or the process is running that program.  It is also important to protect that file, so make sure directory, file permissions are such that the file is unlikely to be deleted.   The GAS uses a a variant of the file technique, see this troubleshooting tip for GAS.

We do actually have an implementation using com.Util.UniqueApplicaitonInstance. Note it puts the check and write into a single call which protects against the scenario where two processes are trying to acquire the token at exactly the same time.   Putting information into this file involves writing to that file, and to remove the lock before program ends you can delete that file. At a minimum, your program consists of …

IMPORT com
...
IF com.Util.UniqueApplicationInstance(filename) THEN
   # CONTINUE
ELSE
   # ERROR and/or EXIT PROGRAM ...
END IF

For a solution using a database table, the token is a row in a database table …

  • at program start (as a database transaction):
    • select a row from a table that indicates if a program is in use
    • if that row does exist then
      • exit the program with an appropriate error message
    • else
      • insert a row into the table and carry on running the program
  • at program end:
    • delete that row from the database table.
  • implement a recovery mechanism to cater for the case where a program ends unexpectedly:
    • when the row is created include information that allows you to unique identify what program is running
    • provide a mechanism so that you can if the program is still running
      • if it is not running, delete the row
  • protect that database table from being changed unintentionally.

… the key things are to make sure you do  this logic inside a database transaction, you want the select and insert to be atomic, and you don’t want a rollback to undo your insert .  You can have additional columns in the table to apply criteria for the restriction, and to help with the recovery criteria.  It also requires your program to make a database connection at startup.

With these techniques, it is the responsibility of the program to protect itself, it is not the responsibility of the menu program to only start a program once.

The technique I was brought up with used a database table, if I was doing it again I would investigate com.Util.UniqueApplicationInstance more.