• Algol 68 / Genie - exiting: action failed, sigpipe_handler

    From Janis Papanagnou@[email protected] to comp.lang.misc on Tue Sep 16 16:39:19 2025
    From Newsgroup: comp.lang.misc

    Picking just an initial sequence of the created output lines issues an
    error diagnostic:

    $ genie -x 'FOR i TO 2000 DO print ((i, newline)) OD' | head -2
    +1
    +2
    a68g: exiting: action failed (a68g-bits.c: 196, sigpipe_handler)

    And defining event handler (say, 'on transput error' or 'on file end')
    in the Genie Algol 68 source doesn't catch that diagnostic, it seems?

    Usually I'm annoyed by such diagnostics in application cases like the
    pipe constructs on OS/shell level.


    Compared to using other Unix processes as data source, with no error:

    $ yes | head -2
    y
    y

    $ seq 2000000 | head -2
    1
    2

    $ awk 'BEGIN {while(1) print c++}' | head -2
    0
    1

    $ curl -# algol68.gridbug.de/algol68.vim | head -2
    " Vim syntax file
    " Language: Algol 68


    On the other hand (with some of above processes in a pipe-sequence):

    $ yes | awk '{print c++}' | head -2
    0
    1
    yes: standard output: Connection reset by peer
    yes: write error


    I wondered whether it makes sense to have the diagnostics. So I added
    actions (with side effects) after the output statements (in the Algol
    sample and in the first Awk sample above), namely printing a message
    on the error channel; the messages didn't appear, i.e. the programs
    got aborted (of course) because they couldn't finish their preceding (output-)operations.

    Should there be a diagnostic message or not, should such diagnostic
    be interceptable by an event handler or not?

    What's the rationale for Awk not printing a diagnostic message, but
    Genie/Algol 68 does?

    Janis

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From richard@[email protected] (Richard Tobin) to comp.lang.misc on Tue Sep 16 18:55:54 2025
    From Newsgroup: comp.lang.misc

    In article <10absqn$2kub3$[email protected]>,
    Janis Papanagnou <[email protected]> wrote:

    What's the rationale for Awk not printing a diagnostic message, but >Genie/Algol 68 does?

    The default unix behaviour is for a process to simply exit without
    printing any message when it receives a SIGPIPE, which is exactly what
    you want for the kind of pipelines you showed.

    In my view that's the correct default behaviour. The receiving end of
    a pipe closing is normal behaviour, not an error - unlike, say, being
    unable to write a file because the disk is full.

    You can imagine cases where this isn't what you want, so it's
    reasonable for a language to provide a way for a program to set the
    SIGPIPE handler or for it to intercept SIGPIPE itself and expose it as
    an exception (as Python does). Intercepting it, printing an error
    message, and exiting does not seem useful.

    -- Richard
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Andy Walker@[email protected] to comp.lang.misc on Wed Sep 17 17:39:09 2025
    From Newsgroup: comp.lang.misc

    On 16/09/2025 15:39, Janis Papanagnou wrote:
    Picking just an initial sequence of the created output lines issues an
    error diagnostic:
    $ genie -x 'FOR i TO 2000 DO print ((i, newline)) OD' | head -2
    +1
    +2
    a68g: exiting: action failed (a68g-bits.c: 196, sigpipe_handler)
    And defining event handler (say, 'on transput error' or 'on file end')
    in the Genie Algol 68 source doesn't catch that diagnostic, it seems?

    Indeed; there is no error in the A68G.

    Usually I'm annoyed by such diagnostics in application cases like the
    pipe constructs on OS/shell level.

    I think the root problem is that there are too many ways a pipe
    can fail, some of which are expected and some not, so that "SIGPIPE" is
    being expected to guess whether there has been an error. I agree with
    Richard [nearby article] that the Unix/Linux default is to fail silently,
    and that this is commonly [but not always] the appropriate behaviour.
    But Marcel seems always to have taken the view that errors should be
    reported. In cases like the present, it's difficult to know whether
    there has been an error or not, so A68G reports the "SIGPIPE" anyway.
    I have an impression that it's "head" or Linux that is inconsistent;
    note that for small values of 2000 ["FOR i to 20 ..."] or large values
    of -2 ["head -1998"] there is no "SIGPIPE". Also, there is no problem
    reported with "tail" ["... | tail | head -2" or even "tail 3000", or
    even "... | head -2 | tail"]. I assume there are differences in what
    is buffered, leading to differences in whether "SIGPIPE" is raised?

    [...]
    Should there be a diagnostic message or not, should such diagnostic
    be interceptable by an event handler or not?

    Pass. But it would be nice if it was consistent!

    What's the rationale for Awk not printing a diagnostic message, but Genie/Algol 68 does?

    Aho et al believe in silent errors, Marcel believes you should
    be told?
    --
    Andy Walker, Nottingham.
    Andy's music pages: www.cuboid.me.uk/andy/Music
    Composer of the day: www.cuboid.me.uk/andy/Music/Composers/Mozart,L
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@[email protected] to comp.lang.misc on Wed Sep 17 23:32:14 2025
    From Newsgroup: comp.lang.misc

    On 17.09.2025 18:39, Andy Walker wrote:
    On 16/09/2025 15:39, Janis Papanagnou wrote:
    Picking just an initial sequence of the created output lines issues an
    error diagnostic:
    $ genie -x 'FOR i TO 2000 DO print ((i, newline)) OD' | head -2
    +1
    +2
    a68g: exiting: action failed (a68g-bits.c: 196, sigpipe_handler)
    And defining event handler (say, 'on transput error' or 'on file end')
    in the Genie Algol 68 source doesn't catch that diagnostic, it seems?

    Indeed; there is no error in the A68G.

    Usually I'm annoyed by such diagnostics in application cases like the
    pipe constructs on OS/shell level.

    I think the root problem is that there are too many ways a pipe
    can fail,

    What different ways (that are worthwhile to be reported) are you
    thinking about here?

    Mind that we are speaking about a simple, well understood mechanism;
    it's generated if the reading communication partner stopped reading
    and terminated or closed the pipe, while the writer continues to
    write into the abandoned pipe. If the reader has finished his task
    and if the writer produces something that's not asked for any more
    I see no pressing need to terminate with an error diagnostic. (What
    do we gain by that message in a pipe context?)

    some of which are expected and some not, so that "SIGPIPE" is
    being expected to guess whether there has been an error.

    ITYM the applications have to decide whether to react to SIGPIPE.

    In the examples the pipe was created externally by the shell. And it
    was not the Algol 68 application that was the _cause_ of the effect,
    the closing of the pipe. If [in the Algol 68 application] a write to
    'stdout' fails then it should at least be possible to intercept that
    event by the standard "transput" event handlers of Algol 68 or Genie
    (IMHO).

    I agree with
    Richard [nearby article] that the Unix/Linux default is to fail silently,
    and that this is commonly [but not always] the appropriate behaviour.

    What Richard wrote (and concludes) matches my gut feelings.

    I'd be interested to hear some standard use-case examples where an
    intercept of the signal to produce a diagnostic message would be
    "appropriate behaviour". - This is what I think Richard also said
    in his last paragraph; please correct me if I misunderstood you,
    Richard.

    But Marcel seems always to have taken the view that errors should be reported. In cases like the present, it's difficult to know whether
    there has been an error or not, so A68G reports the "SIGPIPE" anyway.

    What do we gain with that error message? (In what circumstances?)

    I have an impression that it's "head" or Linux that is inconsistent;
    note that for small values of 2000 ["FOR i to 20 ..."] or large values
    of -2 ["head -1998"] there is no "SIGPIPE". Also, there is no problem reported with "tail" ["... | tail | head -2" or even "tail 3000", or
    even "... | head -2 | tail"]. I assume there are differences in what
    is buffered, leading to differences in whether "SIGPIPE" is raised?

    I don't see any problem here with the behavior of 'head' or 'tail'
    in your examples. Also not with Linux; the pipes seem to work as
    designed [in UNIXes] - the reason for the signal raise is clearly
    described; see above.

    If you have buffers large enough for any specific data then there's
    of course no signal. Or if the applications use own internal buffers
    (see following 'sort' example) there's obviously also no problem.

    If the input gets completely written then all is okay; compare, e.g.,

    $ genie -x 'FOR i TO 2000 DO print ((i, newline)) OD' | sort | head -2
    +1
    +2

    $ genie -x 'FOR i TO 2000 DO print ((i, newline)) OD' | cat | head -2
    +1
    +2
    a68g: exiting: action failed (a68g-bits.c: 196, sigpipe_handler)

    In the first case 'sort' needs the complete input to perform its task,
    while 'cat' just passes data along as they appear. - So the buffering
    effects are [to me] not mystic or anything, and do not quite explain
    (IMO) why an error message would make sense.


    I'm more puzzled by the results of my original examples with 'yes' and
    'awk' combined, where each single tool does not produce the diagnostic.
    (I probably should have a look into the source of 'yes' whether there's
    a hint on the difference in those two cases with 'yes'.) - But that's,
    OTOH, also not the main point of my "Algol 68 / Genie" related post.
    (But maybe it nonetheless helps identifying a rationale for the Genie's behavioral case.)

    Janis

    [...]


    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Alan Bawden@[email protected] to comp.lang.misc on Thu Sep 18 01:14:41 2025
    From Newsgroup: comp.lang.misc

    Janis Papanagnou <[email protected]> writes:

    On 17.09.2025 18:39, Andy Walker wrote:
    > I think the root problem is that there are too many ways a pipe
    > can fail,

    What different ways (that are worthwhile to be reported) are you
    thinking about here?

    I can't tell what anybody is thinking about anything here, and I have no
    idea about how your Algol runtime might actually be implemented, but
    perhaps the following story about SIGPIPE and its history might prove
    helpful:

    SIGPIPE is not just signalled when you write to a pipe with no reader, it
    is also signalled when you write to a network connection that has been
    shut down.

    This is arguably a botch on the part of the folks that added networking
    support to Unix. Network sockets and pipes behave similarly in a lot of situations, so somebody thought that it would be natural to re-use
    SIGPIPE for the case of writing to a broken network connection.
    Unfortunately the default action for SIGPIPE, which is to silently
    terminate the process, isn't suitable for network programming where
    network connections get broken all the time. (If I'm doing some
    transaction to a remote database, should my process _silently_ exit when
    the remote server crashes? Probably not!)

    So on Unix, if you are a C programmer writing a program that expects to
    talk over the network in any way, you must either (a) install your own
    handler for SIGPIPE, or (b) set SIGPIPE to be ignored, and then check for
    the EPIPE error after every call to to the write() system call. Since
    you already have to check for _other_ errors after every call to write(),
    most people choose (b). (Also, writing signal handlers is tricky.)

    So what are you supposed to do if you are writing the runtime support for
    some programming language such as Algol (or alternatively Python)? Well
    a lot of the libraries that your users will want to use will be making
    network connections, so those libraries will need a consistent answer
    about how SIGPIPE handling is configured. Your best choice (IMHO), as
    the runtime author, is to alter the Unix default and set SIGPIPE to be
    ignored. Then each library can handle EPIPE errors as it sees fit. That
    is what the Python runtime does, and what I expect the Algol runtime does
    as well. (It might not, but this is certainly the most straightforward
    way to go.)

    Now what is a Python (or Algol) programmer using such a runtime supposed
    to do if they are writing a program that wants to behave well as part of
    a shell pipeline? In my own experience when writing tools in Python, I
    have found there are two ways to go:

    Sometimes I know that I won't be using any libraries that will need to
    access the network. In that case I just reconfigure the Python runtime
    back to the default Unix SIGPIPE behavior by initially calling
    signal(SIGPIPE, SIG_DFL) myself. Of course, if I do this, there is a
    danger that I will overlook some library that sometimes uses the network.

    The alternative is to leave SIGPIPE as the runtime configured it, and
    check for EPIPE every time I write to standard output. In Python that
    means finding the right places to set up try/except blocks to handle BrokenPipeError exceptions. This is usually not that hard. Perhaps I
    just define, and consistently use, a utility like:

    def output(msg):
    try:
    return sys.stdout.write(msg)
    except BrokenPipeError:
    sys.exit(os.EX_OK)

    You might think that a Python BrokenPipeError exception is just the
    direct manifestation of an underlying Unix SIGPIPE signal, but that is
    _not_ the case. A BrokenPipeError exception is only raised by the Python runtime when a call to the write() system call returns an EPIPE error
    code. So in fact you can't get a BrokenPipeError unless SIGPIPE is
    actually being ignored! Again, I expect that the Algol runtime is doing something similar.
    --
    Alan Bawden
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@[email protected] to comp.lang.misc on Thu Sep 18 09:37:11 2025
    From Newsgroup: comp.lang.misc

    On 18.09.2025 07:14, Alan Bawden wrote:
    Janis Papanagnou <[email protected]> writes:

    On 17.09.2025 18:39, Andy Walker wrote:
    > I think the root problem is that there are too many ways a pipe
    > can fail,

    What different ways (that are worthwhile to be reported) are you
    thinking about here?

    I can't tell what anybody is thinking about anything here, and I have no
    idea about how your Algol runtime might actually be implemented, but
    perhaps the following story about SIGPIPE and its history might prove helpful:

    SIGPIPE is not just signalled when you write to a pipe with no reader, it
    is also signalled when you write to a network connection that has been
    shut down.

    Yes. (And that's IMO still okay, or rather, not bad per se. - It
    might have helped to control proliferation of methods for similar
    cases.)

    Using sockets (and client/server communication) was actually also
    the direction I was thinking when seeking a rationale. (But yet I
    hadn't found a compelling explanation.)


    This is arguably a botch on the part of the folks that added networking support to Unix. Network sockets and pipes behave similarly in a lot of situations, so somebody thought that it would be natural to re-use
    SIGPIPE for the case of writing to a broken network connection.
    Unfortunately the default action for SIGPIPE, which is to silently
    terminate the process, isn't suitable for network programming where
    network connections get broken all the time. (If I'm doing some
    transaction to a remote database, should my process _silently_ exit when
    the remote server crashes? Probably not!)

    Here's where my thinking deviates. In two respects. - On the higher
    protocol level, for DB access I'd expect transactions, that handle a
    complete query (with possible retries, rollbacks, etc.) safely. On
    the low protocol levels (or on the protocol stacks level; cf. OSI)
    I'd expect the communication failures be handled on the appropriate
    protocol stack level.

    I agree that a server crash (or communication problem) should not
    lead to a client exit. - As I've worked a couple years in client-
    server software development I'd like to add that [in our contexts]
    there's also a different quality level on server and communication
    side; there's also redundant servers and communication paths, and
    the servers had a very high degree of quality (whereas the clients
    could stem from "any source").

    As said, these more complex system environments was also my basic
    approach to seek for explanations.

    Incidentally, Genie has (beyond the Algol 68 standard) support for
    "Linux Extensions", namely to explicitly use pipes (an own type is
    available for that) and to create processes with pipe communication
    channels. - There may be some rationale to be found by this support
    for the pipe message, but I can't tell since I've not yet used these
    features. Generally I'd expect here that interception of the signal
    notice should be supported then, and in Genie/Algol 68 consequently
    by the event handlers mechanism. There's already 'on transput error'
    advertised as "This event is caused when an error occurs in transput
    that is not covered by the other events". But maybe a more consequent
    approach would be necessary in the first place; to support signals,
    and signal handling? - To be honest, I'm not sure.

    [ Thorough ruminations about implementation; C, Python, Algol 68 ]

    Thanks.

    Janis

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From richard@[email protected] (Richard Tobin) to comp.lang.misc on Thu Sep 18 11:23:43 2025
    From Newsgroup: comp.lang.misc

    In article <10af9cv$3fhps$[email protected]>,
    Janis Papanagnou <[email protected]> wrote:

    I'd be interested to hear some standard use-case examples where an
    intercept of the signal to produce a diagnostic message would be
    "appropriate behaviour".

    I don't know about "standard", but the obvious case where you don't
    want to just exit is where you need to clean up first, for example
    deleting temporary files, or closing down other communications in an
    orderly manner.

    And pipes can be used between processes for purposes other than
    filters. The upstream process may know that the downstream process
    exiting early is a real error, and need to report it.

    The point of SIGPIPE - and the reason it exists, while there's no
    SIGDISKFULL or SIGWRITEERROR - is to make the common case simple.

    -- Richard
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Lawrence =?iso-8859-13?q?D=FFOliveiro?=@[email protected] to comp.lang.misc on Thu Sep 18 21:35:29 2025
    From Newsgroup: comp.lang.misc

    On Thu, 18 Sep 2025 01:14:41 -0400, Alan Bawden wrote:

    In that case I just reconfigure the Python runtime back to the
    default Unix SIGPIPE behavior by initially calling signal(SIGPIPE,
    SIG_DFL) myself.

    ...

    You might think that a Python BrokenPipeError exception is just the
    direct manifestation of an underlying Unix SIGPIPE signal, but that
    is _not_ the case. A BrokenPipeError exception is only raised by the
    Python runtime when a call to the write() system call returns an
    EPIPE error code. So in fact you can't get a BrokenPipeError unless
    SIGPIPE is actually being ignored!

    ldo@theon:~> python3 -c "print('hi there')" | cat /dev/null
    Exception ignored on flushing sys.stdout:
    BrokenPipeError: [Errno 32] Broken pipe

    Note that my one-line Python program does not include any signal()
    call to ignore SIGPIPE, yet BrokenPipeError does get raised (and
    reported).
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From richard@[email protected] (Richard Tobin) to comp.lang.misc on Fri Sep 19 16:22:30 2025
    From Newsgroup: comp.lang.misc

    In article <10ahtv1$4il2$[email protected]>,
    Lawrence D�Oliveiro <[email protected]d> wrote:

    You might think that a Python BrokenPipeError exception is just the
    direct manifestation of an underlying Unix SIGPIPE signal, but that
    is _not_ the case. A BrokenPipeError exception is only raised by the
    Python runtime when a call to the write() system call returns an
    EPIPE error code. So in fact you can't get a BrokenPipeError unless
    SIGPIPE is actually being ignored!

    ldo@theon:~> python3 -c "print('hi there')" | cat /dev/null
    Exception ignored on flushing sys.stdout:
    BrokenPipeError: [Errno 32] Broken pipe

    Note that my one-line Python program does not include any signal()
    call to ignore SIGPIPE, yet BrokenPipeError does get raised (and
    reported).

    A call to write will get EPIPE if either SIGPIPE is ignored, or
    if there is a handler for SIGPIPE and it returns.

    It won't get EPIPE is the action of SIGPIPE is SIG_DFL, or (obviously)
    if the handler doesn't return.

    -- Richard
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Andy Walker@[email protected] to comp.lang.misc on Fri Sep 19 19:08:14 2025
    From Newsgroup: comp.lang.misc

    On 17/09/2025 22:32, Janis Papanagnou wrote:
    On 17.09.2025 18:39, Andy Walker wrote:
    On 16/09/2025 15:39, Janis Papanagnou wrote:
    Picking just an initial sequence of the created output lines issues an
    error diagnostic:
    $ genie -x 'FOR i TO 2000 DO print ((i, newline)) OD' | head -2
    +1
    +2
    a68g: exiting: action failed (a68g-bits.c: 196, sigpipe_handler)
    [...]
    I think the root problem is that there are too many ways a pipe>> can fail,
    What different ways (that are worthwhile to be reported) are you
    thinking about here?

    I'm thinking about that the A68G loop cannot tell whether

    -- the pipe has closed because "head -2" has said, in effect, "thanks
    for that, I've seen enough", or
    -- because "head" has stopped for some other reason, such as an interrupt
    or because its output has caused a command further downstream to
    fail, or
    -- because "head" has failed, eg because of a bug in "head", or
    -- because the loop has produced faulty output that "head" cannot
    process, or
    -- something else I've not thought of.

    Some of those are expected, some not, and we don't know in general what
    should be done. The case of database transactions needing to be undone
    or redone has been mentioned. Or the A68 may have created files that
    need to be tidied up, either by the SIGPIPE handler or by hand. Perhaps
    worth noting that in the given case, the loop does not terminate after
    going round twice; there are some 30-odd lines of output that are lost
    in the buffering before the SIGPIPE is raised. Perhaps also worth noting
    that the problem is nothing [directly] to do with Algol; "a68g" is a /C/ program.

    Mind that we are speaking about a simple, well understood mechanism;
    it's generated if the reading communication partner stopped reading
    and terminated or closed the pipe, while the writer continues to
    write into the abandoned pipe. If the reader has finished his task
    and if the writer produces something that's not asked for any more
    I see no pressing need to terminate with an error diagnostic. (What
    do we gain by that message in a pipe context?)

    In the case of "head", the error message is redundant. Not so
    in general. You talk about "a pipe context"; /you/ can see that the
    A68G is running in a pipe, but the components of the pipe don't know
    what they are connected to, upstream or downstream. Marcel takes the
    view, it seems, that any error should be reported, rather than trying
    to guess whether you will gain anything.

    [...] If [in the Algol 68 application] a write to
    'stdout' fails then it should at least be possible to intercept that
    event by the standard "transput" event handlers of Algol 68 or Genie
    (IMHO).

    Perhaps, but do bear in mind that it's not [in this case] the
    third line of output that triggers the failure, but some 30+ lines
    later [on my system, at least]. Sorting that out is non-trivial.
    --
    Andy Walker, Nottingham.
    Andy's music pages: www.cuboid.me.uk/andy/Music
    Composer of the day: www.cuboid.me.uk/andy/Music/Composers/Forbes
    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Janis Papanagnou@[email protected] to comp.lang.misc on Sat Sep 20 00:02:38 2025
    From Newsgroup: comp.lang.misc

    On 19.09.2025 20:08, Andy Walker wrote:
    [...] Perhaps also worth noting
    that the problem is nothing [directly] to do with Algol;

    Sure.

    "a68g" is a /C/ program.

    [...]

    Thanks.

    Janis

    --- Synchronet 3.21a-Linux NewsLink 1.2
  • From Alan Bawden@[email protected] to comp.lang.misc on Fri Sep 19 19:03:32 2025
    From Newsgroup: comp.lang.misc

    Lawrence D’Oliveiro <[email protected]d> writes:

    On Thu, 18 Sep 2025 01:14:41 -0400, Alan Bawden wrote:

    > ... So in fact you can't get a BrokenPipeError unless SIGPIPE is
    > actually being ignored!

    ldo@theon:~> python3 -c "print('hi there')" | cat /dev/null
    Exception ignored on flushing sys.stdout:
    BrokenPipeError: [Errno 32] Broken pipe

    Note that my one-line Python program does not include any signal()
    call to ignore SIGPIPE, yet BrokenPipeError does get raised (and
    reported).

    Your program doesn't need to call signal(SIGPIPE, SIG_IGN) because Python
    does that for you -- precisely so that BrokenPipeError will work as expected. See <https://docs.python.org/3.13/library/signal.html#general-rules>.

    Also see <https://docs.python.org/3.13/library/signal.html#note-on-sigpipe>, which says some of the same things I said in my original message. (Although
    it suggests that you should _not_ risk doing signal(SIGPIPE, SIG_DFL), as I sometimes do...)
    --
    Alan Bawden
    --- Synchronet 3.21a-Linux NewsLink 1.2