Beefy Boxes and Bandwidth Generously Provided by pair Networks
Think about Loose Coupling
 
PerlMonks  

Are Addresses From refaddr Unique Across Threads?

by aecooper (Acolyte)
on Feb 14, 2010 at 17:49 UTC ( [id://823155]=perlquestion: print w/replies, xml ) Need Help??

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

Hi all :-),

I recently decided to convert one of my more complex library classes over to the outside-in class model. I'm using Perl 5.8 and rather than use say Class::Std (does that even work with early 5.8?) and add another dependency to my library for the sake of 10 lines of code, I have implemented it directly. I used XDG's posting Threads and fork and CLONE, oh my! as the basis.

Anyway, it's all working as it should :-). However one thing I did differently was to cache the address inside the blessed object rather than `recompute/fetch' it with refaddr() each time I needed it. So instead of doing something like:

my %class_records; my $class_objects; . . sub new($) { my $class = (ref($_[0]) ne "") ? ref($_[0]) : $_[0]; my $this = {attr1 => "Hello World"}; my $self = bless({}, $class); my $id = refaddr($self); $class_records{$id} = $this; $class_objects{$id} = $self; weaken($class_objects{$id}); return $self; } sub some_method($) { my $self = $_[0]; my $this = $class_records{$refaddr($self)}; ... }
I did:
my $class_name = __PACKAGE__; my %class_records; my $class_objects; . . sub new($) { my $class = (ref($_[0]) ne "") ? ref($_[0]) : $_[0]; my $this = {attr1 => "Hello World"}; my $self = bless({}, $class); my $id = refaddr($self); $self->{$class_name} = $id; $class_records{$id} = $this; $class_objects{$id} = $self; weaken($class_objects{$id}); return $self; } sub some_method($) { my $self = $_[0]; my $this = $class_records{$self->{$class_name}}; ... }
Note: The %class_objects hash is used to keep track of objects and refiling them during cloning. It is equivalent to XDG's %REGISTRY hash.

Since I never use refaddr() again on an object once it is created do I need to bother with the refiling that goes on in his CLONE() method when it comes to threading (ithread)? I think not... My reasoning is this:

  • refaddr() returns the memory address associated with that object.
  • Threads within a process all share the same address space. Therefore the address returned from creating a new object and using refaddr() on it will not clash with any existing object.
  • There would be no clash between the main or parent thread and a newly created child thread as %class_records and %class_objects are cloned as are the items that they point to. Although the keys will remain unchanged, what they point to will be unique.
  • The only issue I can see is that if an object that existed in the parent thread when the child thread was created, was then subsequently destroyed in the parent and then the child creates a new object then it could theoretically get the same address as the one destroyed in the parent and clash with the entry inside the child's %class_records and %class_objects maps because the child still has the clone of the object destroyed in the parent.
This issue can easily be overcome with a simple clash detector and then there is no need for CLONE() nor %class_objects. After all refaddr is being used to generate unique ids cheaply.

Is my thinking sound or have I got inadvertently eaten some dodgy mushrooms! :-)

Anyway, many thanks in advance.

Tony.

Replies are listed 'Best First'.
Re: Are Addresses From refaddr Unique Across Threads?
by Xiong (Hermit) on Feb 15, 2010 at 08:17 UTC

    I don't like this caching refaddr() inside the object. One of the purposes of inside-out is airtight encapsulation and although I don't see straight off how this can break that, it smells funny to me. Nor do I see the good use of it; why not just use refaddr() as usual?

    While I applaud novel approaches in general, my appreciation is inversely related to scope. When you reach the level of a whole scheme or concept for all classes, you come to a point where I expect any novelty to come with a great reward. I'm not even sure that inside-out pays out sufficiently, considering that so many old classes are HoH-based.

Re: Are Addresses From refaddr Unique Across Threads?
by shmem (Chancellor) on Feb 15, 2010 at 17:34 UTC

    For a thread-safe inside-out implementation which implements tight encapsulation and black box inheritance, check out Alter by Anno.

Re: Are Addresses From refaddr Unique Across Threads?
by JavaFan (Canon) on Feb 15, 2010 at 18:58 UTC
    True inside-objects, your classes are not.

    One of the points of inside-out objects is that the references do not point to anything used by the inside-out classes. Only the address is used, and nothing else. Your objects rely on the fact they are hash references, and rely on the key __PACKAGE__. This means you are breaking encapsulation. A subclass can fiddle with the internals (by accident) and you are denying your subclasses any implementation that deviates from being a hashref.

    Threads within a process all share the same address space.
    Is that true on every platform? Will that always be true, even in a next version or Perl? I don't think Perl gives you that promise; I would hence not rely on it.

    But if you're going to store the keys in the object themselves, why bother with the refaddress of which you aren't sure it's going to work? Just use a random number, and if it's already used in %class_records, pick another one. Or use a counter.

    BTW, from your code fragment, I don't understand what the role of $class_objects is.

    Of course, the easy way is to upgrade to 5.10. Then you can use fieldhashes, and everything will be done for you.

      Is that true on every platform? Will that always be true, even in a next version or Perl? I don't think Perl gives you that promise; I would hence not rely on it.

      It has to be. You can't have two objects at the same address in the same process.

      Of course, you could have modules that provide the same interface as threads without actually using threads. In fact, such a module already exists.

      A reply falls below the community's threshold of quality. You may see it by logging in.

      Threads within a process all share the same address space.

      Is that true on every platform? Will that always be true, even in a next version or Perl?

      I don't think Perl gives you that promise;

      Yes. Yes. And it is not within the auspices of Perl to make or break that promise. They would not be threads otherwise, as that is the very definition of threads.

      I conceed that it is possible that someone could implement something and call it "threads" that would not satisfy that definition, but it would be a very obtuse thing to do.


      Examine what is said, not who speaks -- Silence betokens consent -- Love the truth but pardon error.
      "Science is about questioning the status quo. Questioning authority".
      In the absence of evidence, opinion is indistinguishable from prejudice.
Re: Are Addresses From refaddr Unique Across Threads?
by aecooper (Acolyte) on Feb 19, 2010 at 20:08 UTC
    Hi all,

    Well firstly many thanks for all your responses you have all been most helpful :-).

    For brevity I shall refer to the traditional refaddr() on every access the refaddr approach and what I have proposed as the caching approach.

    To reply to some of the comments made...

    Why Not Use refaddr() Directly On An Empty Hash?

    Well in the article that I referenced, someone posted performance results comparing various techniques. When compared against the classic basic blessed hash ref class, the traditional inside-out, refaddr approach ran 37% slower as against the caching variety only running 4% slower. Also using refaddr() every time you want to work out your hash key makes it very sensitive to cloning issues during multi-threading. This requires you to have a CLONE method and the %class_objects hash. Using the caching approach is not affected by the cloning issue and is much simpler and faster. As my class is called 1000s of times a second, performance is an issue.

    The data cannot be exposed any more than it can with the traditional refaddr() approach as %class_records is private to the file containing the class (only thing in that file BTW). Anyway the caller could still get the key with the refaddr() approach, its just slightly less obvious.

    One Can Compromise Che Object

    The only thing that could be compromised is the key. The worst case is that a bit of memory is leaked when someone tinkers around with the internals of the public object. The actual %class_records hash is only accessible from within the class. The refaddr approach does not suffer from this memory leak problem because all they could do to affect the refaddr of the hash is to delete it which will call the destructor.

    However accidental corruption is not very likely unlike in languages like C and C++. As for deliberate tinkering they would either quickly loose interest (as it doesn't gain them anything) or change the source code.

    Use Of Hashes Containing Fields

    Well this is a base class. As for any derived classes they are either going to be basic hash refs (ok could get a field clash but very unlikely as most people doing OO in perl tend to find out about what they are deriving from and are very unlikely to use a field named after the base class), something similar to this one (so will just add another field) or refaddr style ones (wouldn't care). When implementing a base class a blessed reference is the most derivation friendly when compared to code, array and scalar refs.

    Basically perfection and complexity vs 98% solution that is a lot faster and simpler...

    Why Not Just Use A Counter Or A Random Number?

    Hehe yup point taken :-), probably would have done that at some point...

    Why Not Upgrade To Perl 5.10?

    A lot of distros are still on 5.8 (RedHat, Debian, CentOS, SlackWare?) and I have deliberately developed this software on an oldish distro so that users don't have to go through patching/upgrade hell just to get a library and GUI application working.

    Many thanks once again. I will be using a counter/random number approach and check for clashes on object construction.

    Yours sincerely,

    Tony.

      Minor point:
      A lot of distros are still on 5.8 (RedHat, Debian, CentOS, SlackWare?)

      I don't know about the others, but at least for Debian this is untrue. The current Debian stable (lenny, released almost exactly a year ago) runs Perl 5.10.0, and the previous stable release (etch) is no longer supported (as of five days ago).


      All dogma is stupid.

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://823155]
Approved by Corion
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others examining the Monastery: (5)
As of 2024-04-24 18:58 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found