finpro has asked for the wisdom of the Perl Monks concerning the following question:

Hi Monks,

I’ve got a script which runs on linux and spawns a thread which opens a FIFO pipe in read only mode. I use a different program to write data to FIFO (written in c or it can be simple echo command).
Works like a charm, however once I exit the script (via CTRL-C) and restart it, the pipe becomes unusable for writing (i.e. no program is able to open it for writing – they get back en error message: errno 26 “Text file busy”). I need to create another FIFO in order for me use the script again.
Internally I catch CTRL-C signal and close the FIFO explicitly.
What’s wrong? Is it because I have 2 threads running i.e. one does some other processing and the second one is responsible for the pipe?? What’s the correct and clean way to exit from multithreaded program in Perl?
Or is it another issue? Any help or ideas would be greatly appreciated.

Thanks,finpro

Replies are listed 'Best First'.
Re: named pipe and "Text file busy"
by tuxz0r (Pilgrim) on Nov 25, 2007 at 06:30 UTC
    I'm trying to recreate your problem, but if this is incorrect, let me know. I can't recreate your particular error, but I think this is what you are trying to do (again, correct me if I'm wrong).

    This script opens the fifo if it doesn't exist and then starts the writer process in the background. It then opens the other end of the FIFO by cat'ing it to /dev/null. IF you don't open both ends of the FIFO, then the open call will block.

    #!/bin/sh # Make the FIFO (first time only) if [ ! -p "file.fifo" ] then mkfifo file.fifo fi # run script to write to FIFO and wait for it to finish ./writefifo.sh & cat file.fifo >/dev/null # open other end wait && echo "writer done" rm -f file.fifo exit 0
    This is the writer script, which simply echo's a line to the FIFO, sleeps a bit and then exits.
    #!/bin/sh echo "a line to the fifo" >> file.fifo sleep 5
    This seems to work fine for me, even when I Control-C the main script before the writer finishes. Do you have any processes still running after your primary Ctrl-C that may be blocking on an open call?

    ---
    echo S 1 [ Y V U | perl -ane 'print reverse map { $_ = chr(ord($_)-1) } @F;'
    Warning: Any code posted by tuxz0r is untested, unless otherwise stated, and is used at your own risk.

      yes, you're right with your example: in my case instead of cat file.fifo >/dev/null i use my Perl script to read the FIFO.
      Your example works fine for me as well, so i believe there's smth wrong with my Perl script.
      I've checked there are no processes left after Ctrl-C the script. Here's the sequence of steps i perform:
      1. Start the script that opens file.fifo and wait for data to arrive
      2. in the shell: echo "test" > file.fifo. This works fine, the script reads "test" with no problems
      3. Ctrl-C my Perl script
      4. do echo "test" > file.fifo again. Result - "Text file busy"
        From your steps, it looks like you start the reader (the perl script), then write something to the file, then kill the reader, then try to write to the file again. I think this is expected behavior (unless the FIFO is opened in non-blocking mode) because an open for reading or writing will block unless another open for the opposite is done (both ends need to be opened, reading and writing). When you kill the reader end of the named pipe your writer blocks because there is no one on the other end to read from it.

        I can't get my examples to recreate the ETXTBSY error, but I suspect that is what this is referring too. If you tail the FIFO in another terminal window and then do that last echo you should work w/o the error.

        ---
        echo S 1 [ Y V U | perl -ane 'print reverse map { $_ = chr(ord($_)-1) } @F;'
        Warning: Any code posted by tuxz0r is untested, unless otherwise stated, and is used at your own risk.

Re: named pipe and "Text file busy"
by TOD (Friar) on Nov 25, 2007 at 04:31 UTC
    if it's a named pipe and it has been created read-only, the "file" still exists after you've interrupted your program. however, Ctrl-C means $SIG{INT} for the process, so by installing a routine which hooks on this signal you should easily remove the pipe prior to exiting the process.
    --------------------------------
    masses are the opiate for religion.
      Thanks for this, but that's what i've got atm. I've installed my routine for $SIG(INT) and inside i close the pipe explicitly, however, this does not help and the behaviour is the same: after the script terminates, doing echo "message" > pipename or trying to write with any other program results in "Text file busy". Under normal circumstances a writing program should just block (unless the pipe is open in non-blocking mode), given that there are no readers (the script has been terminated). Any ideas? Thanks.
Re: named pipe and "Text file busy"
by KurtSchwind (Chaplain) on Nov 25, 2007 at 06:58 UTC

    Do you actually need the FIFO to stay around between runs? Or can you just code a delete before the create if it already exists?

    --
    I used to drive a Heisenbergmobile, but every time I looked at the speedometer, I got lost.
      yes, i do need it to stay between runs, unfortunately :(
Re: named pipe and "Text file busy"
by sgt (Deacon) on Nov 25, 2007 at 21:42 UTC

    There is a contract for the use of a named pipe. I will suppose un*xlike and to simplify I will not talk about non-blocking. For pipes the default is blocking.

  • You need a reader and usually the reader is a long-running process.
  • The reader will block on read as soon as it opens. If there is no reader any writer will block.
  • Remember it *is* a pipe so what gets in must get out... If you write more than a certain maximum number of bytes input is likely to be intermixed between the writers. If the reader does not process the input quickly enough then eventually a writer will be blocked and then all the other writers, so you need some flow analysis or special janitor processes.

    So do you have a permanent reader? If you have a read-once reader the behavior you seem to be experiencing is *normal*. Can it be somehow that the pipe is opened by the two threads? Can you check with lsof or similar the processes that maintain a descriptor opened to the named pipe. If you had posted some code we might have been able to help better.

    % steph@ape (/home/stephan) % % cat reader.sh #!/bin/ksh trap 'exit 0' INT pipe=named_pipe outfile=out [[ -p $pipe ]] || mkfifo $pipe exec 0<$pipe while dd if=$pipe bs=64 count=1 >> $outfile do print .processing sleep 2 done % steph@ape (/home/stephan) % % jobs [1] + Running ./reader.sh &
    cheers --stephan
      Thanks I should note that "text file busy" only happens after i open the pipe with my perl script and terminate it. After that, this error occurs irrespective of whether later i've got the script (reader) running together with the writer or a writer by itself.
      I've checked the pipe is open by one thread only and the descriptor is not maintained after i exit the script.
      The code snippet is quite basic:
      my $alertsthr = threads->new(\&al_thread); $SIG{'INT'} = 'CLEANUP'; sub CLEANUP { print "\nClosing pipe\n"; close PIPE; exit(); } sub al_thread { my $myline; while (1) # keep reading from the pipe until we're killed { print "$config->{FIFO} open\n" if $config->{VERBOSE}; open (PIPE, "$config->{FIFO}") or die "Could not open the input +pipe $config->{FIFO}\n"; while($myline = <PIPE>) { print "read $myline\n" if $config->{VERBOSE}; process_message($myline); } print "$config->{FIFO} EOF\n" if $config->{VERBOSE}; } }

        Is there really any guarantee that the SIGINT will be caught by the right thread? you could try forking which would be ok in this respect.

        Then the ETXTBSY (errno 26) is weird. I see the error only with open(2) but not read(2) or write(2). Cannot reproduce it on hp testdrive.

        cheers --stephan