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

Gentle monks,

Does the :locked attribute not work under 5.8.0? For example, I expected the following

$ perl -Mthreads -le \ > 'sub f :locked {print $_[0]; sleep 2; print $_[0];} > threads->create(\&f, $_) for @ARGV; > $_->join() for threads->list()' foo bar

always to print "foo\nfoo\nbar\nbar\n", just as if I'd manually lock()ed a shared variable at the start of the sub. Instead, the f() calls run on top of each other. (e.g. - foo bar foo bar)

This happens both with redhat's perl-5.8.0-55 and with perl-5.8.1-RC5 on linux 2.4 -- and, yes, $Config{useithreads} eq "define" :)

Replies are listed 'Best First'.
Re: (ithreads) :locked subs
by liz (Monsignor) on Sep 24, 2003 at 09:05 UTC
    I've just submitted a documentation patch for this to p5p, so at least the documentation will clearly state that the "locked" attribute is for 5.005 threads only.

    Still looking at a way to have Perl bomb at compilation when using the "locked" attribute in ithreads. Even if I would find it, I doubt whether Jarkko will allow it this late in the release game ;-(

    Liz

    Update:
    I've just submitted a patch to p5p that will make:

    sub a : locked {}
    die at compilation time with:
    Invalid CODE attribute (with ithreads): locked at line ....
    if your Perl was compiled with ithreads support. As this (small) change to toke.c broke quite a number of tests when testing a threaded perl, I needed to patch 5 test-files to fix the errors. Therefore, I'm doutbful whether it will get included in 5.8.1.

    Update:
    The documentation patch made it into 5.8.1, the patch that would make using :locked a compile error, didn't.

Re: (ithreads) :locked subs
by BrowserUk (Patriarch) on Sep 24, 2003 at 06:56 UTC

    I *think* that it is safe to say that the locked attribute is a pthreads throwover that doesn't serve any purpose with ithreads.

    When you spawn your threads, they each end up with their own seperate copies of your sub f(), along with (almost) everything else in the interpreter.

    P:\test>perl -Mthreads -l $|=1; sub f{ print $_[0]; sleep 5; print $_[0]; } threads->create( \&f, \&f ) for 1..2; $_->join for threads->list; print 'done'; ^Z CODE(0x1bf9b7c) CODE(0x1c23c24) CODE(0x1bf9b7c) CODE(0x1c23c24) done

    As you can see, the purpose of :locked subroutine attribute is completely negated by the design of ithreads.

    The problem is that whilst it is possible at the system (C/OS) level to share code between threads and have seperate copies of data (ie. reentrent code), at the perl interpreter level, it is not. Perl (byte)"code" is simply another form of data at the system level, and is not reentrant. Hence, the ithreads design works around this by creating copies of the entire (system level) data segments which in perl terms means that all user-level code is also replicated. It's an unfortunate fact of life that perl long history means that adapting the current sources to allow true threading at the perl level is not viable, even on platforms that support threading natively. Re-writing the compiler and interpreter to segregate the perl code from the perl data such that perl code would become reentrant would be a nightmare to undertake.

    I think this may be one of the motivations behind Ponie, though that's a guess as I've yet to see it stated, and if it is, it is only one of many,


    Examine what is said, not who speaks.
    "Efficiency is intelligent laziness." -David Dunham
    "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
    If I understand your problem, I can solve it! Of course, the same can be said for you.

      It has been written:

      As you can see, the purpose of :locked subroutine attribute is compl +etely negated by the design of ithreads.

      Well, the purpose (as I understand it) is still valid, I'd say. You certainly want to be able to lock() manipulations of :shared (or shared()) variables. If this simply isn't possible in 5.8, well, bummer.

      Indeed, threads::shared lock()s are BLOCK-based. Very convenient to name those blocks (as subs/methods), and synchronize their execution, automagically.

      (Hmm -- maybe time to get an account, if just for this thread, er, ithreads :)

        The function of mutexing access to shared data hasn't gone away and this function is catered for in iThreads by the use of the :shared variable attribute or threads::shared::shared() function, in conjunction with the threads::shared::lock() function.

        The purpose of the :locked subroutine attribute is no longer valid, as it is impossible to share perl subroutines bewteen threads.

        What my example code attempted to demonstrate is that your attempt to use the :locked attribute to prevent two threads from concurrently executing your sub f(), served no purpose as, despite surface appearance, each thread was in fact getting it's own unique copy of that sub. So even if the locked attribute semaphore code is still operational under ithreads, it would never come into effect as there will be two mutexes applied to two pieces of data (presumable the subs cv entry in the symbol table) and the two entities could never interfere with each other directly.

        However, if the sub uses a shared variables (as in my $var : shared; or my $var; share( $var);), the the presence of the :locked attribute on the subroutine would not only serve no useful purpose, it might actually lull the unsuspecting into believing that they do not need to use the lock() function on the shared variables, which is inherently dangerous.

        I guess that the fact that the :locked subroutined attribute doesn't raise either a syntax or unknown attribute error under iThreads should be considered a bug!


        Examine what is said, not who speaks.
        "Efficiency is intelligent laziness." -David Dunham
        "When I'm working on a problem, I never think about beauty. I think only how to solve the problem. But when I have finished, if the solution is not beautiful, I know it is wrong." -Richard Buckminster Fuller
        If I understand your problem, I can solve it! Of course, the same can be said for you.

Re: (ithreads) :locked subs
by PodMaster (Abbot) on Sep 24, 2003 at 06:25 UTC
    Not exactly. attributes says:
    locked

    Setting this attribute is only meaningful when the subroutine or method is to be called by multiple threads. When set on a method subroutine (i.e., one marked with the method attribute below), Perl ensures that any invocation of it implicitly locks its first argument before execution. When set on a non-method subroutine, Perl ensures that a lock is taken on the subroutine itself before execution. The semantics of the lock are exactly those of one explicitly taken with the lock operator immediately after the subroutine is entered.

    And that's basically what happens (i think you misunderstand what locking does).
    #!/usr/bin/perl -w -l -- @ARGV = qw[ foo bar baz ]; use threads; $|=1; sub f :locked { my $tid = threads->self()->tid(); print "A}$tid}$_[0]"; select undef, undef, undef, rand(2); print "B}$tid}$_[0]"; } threads->create(\&f, $_) for @ARGV; $_->join() for threads->list() __END__ A}1}foo A}2}bar A}3}baz B}3}baz B}2}bar B}1}foo #!/usr/bin/perl -w -l -- @ARGV = qw[ foo bar baz ]; use threads; $|=1; sub f :locked { my $tid = threads->self()->tid(); print "A}$tid}$_[0]"; select undef, undef, undef, rand(2); print "B}$tid}$_[0]"; } threads->create(\&f, $_) for @ARGV; $_->join() for threads->list() __END__ A}1}foo A}2}bar A}3}baz B}1}foo B}2}bar B}3}baz

    MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
    I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
    ** The third rule of perl club is a statement of fact: pod is sexy.

      Hmm...in your example there's no evidence that a lock is "taken on the subroutine itself" -- it's clear that threads 1, 2, and 3 are executing f() concurrently, which is contrary to the purpose of :locking a subroutine.

      Let me rephrase -- I expected :locked subs to function like the docs indicate that the (not available under 5.8) manual lock()ing of a CODEREF (that is, lock(\&f)) used to function. That is, that invocations of ":locked" or ":locked :method" subs would be synchronized and thus serialized, rather like sync'd methods in Java.

      By way of illustration, this is the moral equivalent of what I expected :locked to do:

      #! /usr/bin/perl use threads; use threads::shared; $|=1; { my $l : shared; # private lexical sub f() { lock($l); # any f() in any thread is "locked" my $tid = threads->tid(); print "A}$tid}$_[0]\n"; select undef, undef, undef, rand(2); print "B}$tid}$_[0]\n"; } } # -- end scope for $l threads->create(\&f, $_) for (qw|foo bar baz|); $_->join() for threads->list(); __END__

      Run it and you'll see that the threads are locking each other out. A useful device to prevent shared state corruption, very much like Java's "synchronized" keyword.

      I'm having a hard time reading attributes.pm any other way.

        *foreheadslap* I see what you're saying, but I fear the docs are misleading then(or its a bug). It's not like you can lock(@_) and expect it to work sensibly (whatever that means). BrowserUks got a better beat on it.

        MJD says "you can't just make shit up and expect the computer to know what you mean, retardo!"
        I run a Win32 PPM repository for perl 5.6.x and 5.8.x -- I take requests (README).
        ** The third rule of perl club is a statement of fact: pod is sexy.

Thread::Synchronize
by liz (Monsignor) on Sep 29, 2003 at 15:15 UTC
    It appears that the ": locked" attribute will be eradicated from Perl in 5.10 / Ponie. However, I liked the functionality (previously unknown to me), so I've just implemented Thread::Synchronize which introduces the ": synchronize" code attribute.
    NAME
    Thread::Synchronize - synchronize subroutine calls between threads

    SYNOPSIS
    use Thread::Synchronize;  # activate :synchronize attribute
    
    sub foo : synchronize { } # only one subroutine running at a time
    

    DESCRIPTION
    This module currently adds one feature to threaded pro- grams: the ":synchronize" subroutine attribute which causes calls to that subroutine to be automatically syn- chronized between threads (only one thread can execute that subroutine at a time).

    Actually, this implementation uses a source filter, so no actual attribute handling is involved during runtime.

    Liz

    Update:
    Changed the name of the module to Thread::Synchronized.