While we're on the subject of "mutexes". . .

Continuing the discussion from GoPi5Go-Dave Shines With Turtlebot3 Cartographer:

@cyclicalobsessive
While we’re on the subject of “mutexes”. . . .

Given the following code:

from di_mutex import DI_Mutex

display_mutex = DI_Mutex("Display")


try:
    display_mutex.acquire()
    # do protected mutex access stuff here

finally:
    display_mutex.release()

Assume that I want to test if a lock file exists, and if it does - I want to immediately bail, but if it doesn’t I want to create it and continue.

Also assume that the “protected mutex stuff” is the entire blinkin’ script, what do I do?

What I want to accomplish is more like this:
(psudo-code)

import [whatever I need to import]

# Test to see if I am the first instance
if exists (display_lock_file):  # does the lock file exist?  If so, bail.
    print("A previous instance is already running, exiting.")
    sys.exit(0)
else:
    create(display_lock_file)

# prepare to capture kill signals or ctl-c
create_signal_handler(terminate, interrupt)
(etc. etc. etc.)

# Continue with the script

etc.
etc.
etc.

# special section that receives a terminate or ctl-C
# and gracefully shuts down
when_signal_received(terminate, interrupt):
    clear and close the display
    remove the lock file.
    sys.exit(0)

I’m not sure how to do this with a mutex since all the “protected mutex” stuff is the entire script itself.

Is there a way to do it the way I want?  It seems that no matter what I want to do, I have to import either a mutex library, file handling libraries, or “touch”.

Any ideas?

How about this code snippit?

lock_file_path = "/var/lock/display.lock"

# Check if this script is already running by looking for the
# "lock file" - if it already exists, the script is already running.
try:
    lockfile = open(lock_file_path, "x")
except:
    print("A previous exception is already running. exiting.")
    sys.exit(0)
else:
    lockfile.close()  #close the file - it should still remain in place.

# If we got this far, the lock file didn't exist
# and we're the only instance running.
(etc.)

What do you think of this?

This is what I tried:

lock_file = "/var/lock/display.lock"

# Check if this script is already running by looking for the
# "lock file" - if it already exists, the script is already running.
try:
    lockfile = open(lock_file, "x")
except:
    print("A previous instance is already running. exiting.")
    sys.exit(0)
else:
    lockfile.close()

<snip>

# until some keyboard event is detected
print("Clearing and shutting down display")

# Clear display.
disp.fill(0)
disp.show()
try:
    os.remove("/var/lock/display.lock")
except:
    print("file doesn't exist. Nothing to do.")
finally:
    sys.exit(0)

It works, but would a mutex be a better choice?

Usually:

  • Mutexes (mutual exclusion locks) are used to synchronize access to shared resources within a single process. They ensure that only one thread can access a critical section of code at a time, preventing race conditions and data corruption.
  • File locks are used to synchronize access to files across multiple processes. They prevent multiple processes from reading or writing to the same file simultaneously, ensuring data integrity.

The “DI mutex” is a hybrid of these to be both thread-safe and multiprocess-safe.

I think the snippet you show has a some “technical leak paths”. Some are not a problem, but perhaps a system shutdown could leave the lockfile present?

1 Like

If you mean an abnormal shutdown, absolutely.  But then again, that can happen with mutexes too.

The code is designed to capture the shutdown and terminate signals and clean up behind itself.

One thing that I don’t account for - and I am still researching - is what happens if a child process gets the signal before the main process?  It’s a known race condition and if the child wins, it throws an exception that forces the parent to abend too.

One of the things I did write is a small sub-utility that does two things:

  1. Resets and clears the display if it’s not already reset and cleared.
  2. Unconditionally removes the lockfile if it’s present.

My “startup_script” that runs as a cron job in the system crontab runs “clearOLED”, (the script I just mentioned), “test_servos”, (that moves the robot’s head to indicate boot-up), and launches the status script.

1 Like

Perfect - good work.

1 Like

One additional refinement I made to the status script was to test for, (and replace if necessary), the lock file just in case it was accidentally deleted.  This occurs at the end of each cycle via a try, except, finally block with a “pass” in the except part, (the file exists).

1 Like

Idea:
Create a function “the_entire_blinkin’_script” and then “try” it.

1 Like