Runter
Wizard


Group: Members
Posts: 1,851
Joined: Jun 1, 2006
|
#1 id:28463 Posted Jul 8, 2009, 11:07 pm
|
Just thought I'd outline the use of Weakref class in Ruby. It will almost certainly be useful at some point to a mud developer---and none of my Ruby books mentioned it even.
So you may have came across a problem where you wanted to use references but didn't want to be responsible for having to unlink all the references if the object was removed from the Object Space. (Because the object wouldn't be removed from the Object Space until these references are all removed.)
An easy solution to this problem is a Weak Reference. These act as normal references in Ruby, however, as expected their existence won't prevent the garbage collector from doing its job. So when using weak references you simply check if they are still valid before accessing them. (If they aren't you have to handle that.) Using a weak reference after their dereferenced object has left the Object Space will result in an exception, which can also be rescued pretty easily.
Code (text): 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 |
str = "a string" # we assign str a new object
wr = WeakRef.new(str) # Then create a weak reference to the same object.
str = nil # There are no longer any references to our object.
# So the garbage collector is now free to take it at any time even with a weak reference left.
### Now we order the garbage collector to work immediately.
ObjectSpace.garbage_collect
if wr.weakref_alive?
# This test tells us wr is valid
else
# Weak reference is indeed no longer valid.
end
|
|
......................... CoralMud project
For once you have tasted flight Ruby you will walk the earth with your eyes turned skywards,
for there you have been and there you will long to return. --
Leonardo Da Vinci Yukihiro Matsumoto
Last edited Jul 8, 2009, 11:26 pm by Runter
|
|
|
|
Runter
Wizard


Group: Members
Posts: 1,851
Joined: Jun 1, 2006
|
#3 id:28466 Posted Jul 8, 2009, 11:15 pm
|
David Haley said:As I said on IMC when I proposed these, I still it's a little nicer if the checking is done behind the scenes. If I want to test "actor.follower != nil", I don't really care if it's actually nil or a stale reference -- I just want to get the answer. So I would probably wrap this, either using another class or some kind of function, so that you'd do something like actor.getFollower().
Of course, and since we're talking Ruby here in specific you could wrap it with an accessor method.
|
......................... CoralMud project
For once you have tasted flight Ruby you will walk the earth with your eyes turned skywards,
for there you have been and there you will long to return. --
Leonardo Da Vinci Yukihiro Matsumoto
Last edited Jul 8, 2009, 11:16 pm by Runter
|
|
|
|
Runter
Wizard


Group: Members
Posts: 1,851
Joined: Jun 1, 2006
|
#5 id:28468 Posted Jul 8, 2009, 11:32 pm
|
Code (text): 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35 |
require 'weakref'
class Player
### define some accessors
def follower=(f)
@follower = WeakRef.new(f)
end
def follower
return false if @follower == nil
if @follower.weakref_alive?
@follower
else
false
end
end
end
### create 2 players
p = Player.new
p2 = Player.new
### p2 referenced as the follower of p1
p.follower = p2
### p2 is no longer valid
p2 = nil
ObjectSpace.garbage_collect
### p.follower returns false instead of the follower.
puts p.follower
|
|
......................... CoralMud project
For once you have tasted flight Ruby you will walk the earth with your eyes turned skywards,
for there you have been and there you will long to return. --
Leonardo Da Vinci Yukihiro Matsumoto
Last edited Jul 8, 2009, 11:33 pm by Runter
|
|
Chris Bailey
Wizard

Group: Members
Posts: 602
Joined: Sep 13, 2008
|
#6 id:28470 Posted Jul 9, 2009, 12:28 am
|
Runter - It seems to me an excellent way of tying up loose ends, I'm currently recreating my implementation of several features to take advantage of this. What I'm wondering though are the repercussions of such an implementation. It seems too simple to just be the answer we were looking for. Does anyone with more knowledge than me know of a reason, or way, that this can be bad? Considering of course that the exceptions are properly handled and you take precaution to make sure you aren't using references to objects that have been accidentally cleaned up.
|
|
......................... If what Proust says is true, that happiness is the absence of fever, then I will never know happiness. For I am possessed by a fever for knowledge, experience, and creation.
Last edited Jul 9, 2009, 12:28 am by Chris Bailey
|
|
|
|
Runter
Wizard


Group: Members
Posts: 1,851
Joined: Jun 1, 2006
|
#8 id:28472 Posted Jul 9, 2009, 12:31 am
|
For one thing you have to ensure the garbage collector has been invoked. Otherwise the reference could be valid after you intend it to be squashed.
|
......................... CoralMud project
For once you have tasted flight Ruby you will walk the earth with your eyes turned skywards,
for there you have been and there you will long to return. --
Leonardo Da Vinci Yukihiro Matsumoto
|
|
Runter
Wizard


Group: Members
Posts: 1,851
Joined: Jun 1, 2006
|
#9 id:28484 Posted Jul 9, 2009, 3:45 am
|
flumpy said:FYI the JVM also has weak references. You can use WeakHashMap or WeakReference objects to do the same thing.
Also, Java has another type of reference called a SoftReference, which is similar.
Here's java's definition of Strong, Weak and Soft types:
http://java.sun.com/j2se/1.4.2/docs/api/java/lang/ref/package-summary.html#reachability
Quote:
Reachability
Going from strongest to weakest, the different levels of reachability reflect the life cycle of an object. They are operationally defined as follows:
* An object is strongly reachable if it can be reached by some thread without traversing any reference objects. A newly-created object is strongly reachable by the thread that created it.
* An object is softly reachable if it is not strongly reachable but can be reached by traversing a soft reference.
* An object is weakly reachable if it is neither strongly nor softly reachable but can be reached by traversing a weak reference. When the weak references to a weakly-reachable object are cleared, the object becomes eligible for finalization.
* An object is phantom reachable if it is neither strongly, softly, nor weakly reachable, it has been finalized, and some phantom reference refers to it.
* Finally, an object is unreachable, and therefore eligible for reclamation, when it is not reachable in any of the above ways.
[edit]... interestingly phantom references can occur if you drop out of a scoped block within a method, and do not nullify your object reference. Phantom objects can be the cause of memory leaks if not looked out for !!
Sounds like a good post to go on the Java board. ;)
|
......................... CoralMud project
For once you have tasted flight Ruby you will walk the earth with your eyes turned skywards,
for there you have been and there you will long to return. --
Leonardo Da Vinci Yukihiro Matsumoto
|
|
flumpy
Wizard


Group: Members
Posts: 623
Joined: Mar 27, 2009
|
#10 id:28486 Posted Jul 9, 2009, 3:58 am
|
Chris Bailey said:Runter - It seems to me an excellent way of tying up loose ends, I'm currently recreating my implementation of several features to take advantage of this. What I'm wondering though are the repercussions of such an implementation. It seems too simple to just be the answer we were looking for. Does anyone with more knowledge than me know of a reason, or way, that this can be bad? Considering of course that the exceptions are properly handled and you take precaution to make sure you aren't using references to objects that have been accidentally cleaned up.
I think weak references should really only be used when being a "passive observer" of a system (like the follower example), tracking statistics etc rather than a way to design your system.
For example, in your world object (my registry) you need to make absolutely sure that when you dest an object it is removed, and not removed by something else. This should definitely be strongly referenced. That is a system designed to behave how you would expect: when you want to remove the object from the game, the object is forcibly removed by you. If you made your object world "weak", you may end up gc'ing the object in between the times you remove it from one strong reference and placing it into another (ie, when moving from room to room perhaps) - it's unlikely if you remove the object and then immediately put it into the next strong container, but it could happen. It will definitely happen a lot if you have weak inventory containers too tho.
I expect you *could* use weak references in inventory handling for rooms and players, so that you don't have to unlink it from their inventory when dest'ing. But I have not really made up my mind about this part. I expect I might be unhappy about losing control of whether or not the items need removing myself, and want the surety that they will not "disappear" before I have had a chance to do something with them, or hang around in the memory causing a leak.
The only certain scenario is if you are a passive bystander, observing objects and keeping an eye on whats going on. Anything else I wouldn't be happy with I think.
|
|
|
flumpy
Wizard


Group: Members
Posts: 623
Joined: Mar 27, 2009
|
#11 id:28487 Posted Jul 9, 2009, 3:59 am
|
Runter said:
Sounds like a good post to go on the Java board. ;)
ahh crud, this is the ruby board. sorry chaps. :S post deleted.
|
Last edited Jul 9, 2009, 4:00 am by flumpy
|
|
|
|
David Haley
Wizard


Group: Members
Posts: 6,912
Joined: Jun 30, 2007
|
#13 id:28511 Posted Jul 9, 2009, 9:33 am
|
Right, there's a difference between ownership and reference. A character can reasonably be said to "own" its inventory, but not so of the person it is following or the room it is in. It's the same conceptual problem you have in non-GC'ed languages like C++ where you need to decide who owns the pointer and therefore who is responsible for freeing it.
Regarding forcing the gc to be run, whether or not you want to do that at, say, the end of every main loop is up to your particular language implementation. It could be a fast process or a slow one. I think that Lua lets you disable automatic GC runs, so you could control it yourself if you wanted to. I imagine other languages might have similar features.
Still, it's not too bad to act on something that should be a "dead" character but is still floating around. At worst you might report that you're following somebody who has quit or something like that; it's unlikely to cause a "null pointer exception" or other kind of invalid memory exception (or the equivalent in the relevant language).
|
|
|
Runter
Wizard


Group: Members
Posts: 1,851
Joined: Jun 1, 2006
|
#14 id:28538 Posted Jul 9, 2009, 2:41 pm
|
Quote:
Still, it's not too bad to act on something that should be a "dead" character but is still floating around. At worst you might report that you're following somebody who has quit or something like that; it's unlikely to cause a "null pointer exception" or other kind of invalid memory exception (or the equivalent in the relevant language).
Not sure that's the worst case, but fair enough.
flumpy said:... what I was trying to say earlier (god i really should drink more caffeine in the morning :() was that you shouldn't really use a Weak ref in situations where you "know" the life time of the objects you are tracking. Sorry for the ramble above :D
More true for some languages than others. I mean, yes, there are specific places it has uses and it doesn't make sense to use it in other places. Just keep in mind in Ruby you lose little/nothing to do it. So if there's any benefit it makes sense. They're technically the same thing as a reference (which everything has to be in Ruby).
|
......................... CoralMud project
For once you have tasted flight Ruby you will walk the earth with your eyes turned skywards,
for there you have been and there you will long to return. --
Leonardo Da Vinci Yukihiro Matsumoto
Last edited Jul 9, 2009, 2:42 pm by Runter
|
|
|
|