I am making an interpreter in C, and I'm having a problem with my reference counting.
Each value (which is the interpreter's representation... of a value) is allocated with refcount 0. Once it gets added to the stack, it increments the refcount.
The only way to get a value off the stack is to pop it off it, but that leads to problems. My popping function returns the value that is popped, but if the refcount is 0 and I destroy the value I can no longer return it.
I get that I should probably put the refcount check somewhere else, but that just seems ugly as there are a lot of places that use the popping function.
What can I do to workaround this issue? Is implementing a real GC algorithm necessary in this case?
I use my own data base system which also uses a kind of refcount.
When an object is stored into a data base, then its refcount is incremented. When I get an object from a data base, its refcount remains unchanged. It is decremented only if the object is deleted by any way (usually the deletion of a data base containing it or its replacement by another object in a data base containing it). The object is really destroyed only when its refcount is equal to zero AND its deletion is required.
whenever you create object or value in your case, you should set the refcount to 1. On pushing to the stack, increment it. On poping, decrement. On pop each operation decrement and check th refcount, destroy value if refcount is zero. Which function destoy-value already be doing so you just need to call that function on pop.
As a general rule, increment the count when creating a reference and decrement when deleting a reference. But there's also a third type of transaction (or an optimized composition of the two) where there's just a transfer and you don't change the count at all.
This is the case if you pop the value from the stack and them proceed to use the value (in a local variable, maybe). First the object was on the stack, and now its in a variable; but there's still only one object. The reference count doesn't change until you're done with it and ready to discard the reference.
Related
While reading through the documentation and some SO questions,
I learned that Py_INCREF() should be applied to Py_None and that applying Py_DECREF() to Py_None is a bad idea unless you hold a reference to it.
But when I look up the reference count of Py_None:
from sys import getrefcount
print(getrefcount(None))
It's at 34169. I'd have to Py_DECREF in a loop 30000 times to make my interpreter crash. A single INCREF or DECREF doesnt do anything. Can anybody explain?
There are just a lot of references to None, all over the place. Many of them are implicitly created, like __doc__ for functions and classes with no docstring, or the first element of every code object's co_consts, but plenty are created explicitly. The refcount is a count of those references.
You should not treat None specially when managing references. As with any other object, Py_INCREF and Py_DECREF should be used when you create or destroy a reference to the object in C code, if some other code isn't responsible for that.
I'd have to Py_DECREF in a loop 30000 times to make my interpreter crash.
Okay. So you write a function that mismanages its refcounting and incorrectly decrements None's refcount by 1.
Then your function gets called 30000 times. That's a completely unremarkable number of times to call a function. Your program crashes, quite likely in a location completely unrelated to your broken function, when unrelated reference manipulation somewhere else causes the refcount of None to hit 0 because the value was already too low due to your function.
Or maybe your function only gets called 10000 times. Then, later, when your program is shutting down, most of those None references get cleaned up, and your program crashes messily during shutdown when the refcount of None hits 0 anyway. Maybe that means your program fails to save important data. Maybe it just means your program produces a really unprofessional-looking error message every time it shuts down. Either way, customers are unhappy, and you've got a nasty bug to debug.
Does rust drop the memory after move is used to reassign a value?
What happens to the string "aaa" in this example?
let mut s = String::from("aaa");
s = String::from("bbb");
I'm guessing that the "aaa" String is dropped - it makes sense, as it is not used anymore. However, I cannot find anything confirming this in the docs. (for example, the Book only provides an explanation of what happens when we assign a new value using move).
I'm trying to wrap my head around the rules Rust uses to ensure memory safety, but I cannot find an explicit rule of what happens in such a situation.
Yes, assignment to a variable will drop the value that is being replaced.
Not dropping the replaced value would be a disaster - since Drop is frequently used for deallocation and other cleanup, if the value wasn't dropped, you'd end up with all sorts of leaks.
Move semantics are implicit here. The data in s is initialized by moving from the String produced by String::from("bbb"). The original data stored in s is dropped by side-effect (the process of replacing it leaves it with no owner, so it's dropped as part of the operation).
Per the destructor documentation (emphasis added):
When an initialized variable or temporary goes out of scope, its destructor is run, or it is dropped. Assignment also runs the destructor of its left-hand operand, if it's initialized. If a variable has been partially initialized, only its initialized fields are dropped.
According to LDD3, both kobject_init and kobject_add increase the reference count. So kobject_create_and_add should create an object with 2 references. Right?
But in linux/sample/kobject/kobject-example.c, the module kobject_create_and_add an object, but only put it once, when exiting. It implies there's only one reference count. What's wrong?
So kobject_create_and_add should create an object with 2 references. Right?
Not really. It creates an object (which should set its reference counter to 1) and then adds it to the parent object (which should increment the parent object's reference counter). Two different objects are involved, and two different reference counters are handled.
Calling kobject_put() decrements the reference counter of the object and, when the new reference counter value tests accordingly, calls kobject_release() on the object internally. The latter invokes kobject_cleanup(), which, in turn, invokes kobject_put() on the parent object thus taking care of its reference counter, too.
Can I see how much of my stack has been filled up?
I suppose that if I could see the value of avma, that'd help.
Moreover, if I do an operation on a variable and store the return value in the same variable, does it replace the initial value of the variable?
Does moving the stack pointer up overwrite everything below that point (in case I further fill up the stack), even the assigned variables?
Can I see how much of my stack has been filled up? I suppose that if I could see the value of avma, that'd help.
Just print avma then: printf("avma = %lu", avma);
or %lX if you prefer hexadecimal values.
Moreover, if I do an operation on a variable and store the return value in the same variable, does it replace the initial value of the variable?
The question is too vague. I'm assuming you mean something like
GEN x;
x = fun1();
x = fun2();
Then x points to the new data created by fun2 on the pari stack (GEN is a pointer type) but the old data is still there on the stack, right where fun1 created it. This is no different from what you'd get if the functions above returned malloc'ed memory: the above code would leak memory. That's the very reason you need GC in libpari programming.
Does moving the stack pointer up overwrite everything below that point (in case I further fill up the stack), even the assigned variables?
Yes. There's nothing special about "assigned variables", they are just pointers to some place on the pari stack, indicating where the object is written (actually, the object root).
There is a way to create GEN objects outside of the pari stack (so-called clones) using gclone and those would be unaffected.
I have thought of a GC which I can't see major flaws with, and I am wondering why it isn't used more prevalently, or why I haven't heard of its use.
The system is:
All objects have a 4 byte unsigned int counter attached to them. (Could be 2 bytes, I don't know.
Whenever an object is constructed, its counter is started at 1.
When an object is sent to a function as a parameter, its counter is incremented.
When an object reaches the point where it is no longer used within a function (Could be the very end of scope) its counter is decremented.
When the object's counter reaches zero, it is deleted, as at no position in code is it referenced.
Is there a fringe case in which the counter becomes faulty? What are the disadvantages, and the advantages?
Thank you in advance for your assistance.
Its called reference counting. I would suggest reading the wikipedia article as it covers the advantages and disadvantages.