Garbage management in Pari C - garbage-collection

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.

Related

How did I access the middle element of the stack if you can't access the middle element

I'm reading this document, and it says that The stack stores values in the order it gets them and removes the values in the opposite order. This is referred to as last in, first out. Think of a stack of plates: when you add more plates, you put them on top of the pile, and when you need a plate, you take one off the top. Adding or removing plates from the middle or bottom wouldn’t work as well!
So how does this work
fn main() {
let _x = 1;
let y = 2;
let _z = 3;
println!("{}", y);
}
Thanks for helping, sorry if I'm missing something obvious.
First, they are explaining how a call stack works. When a function a() calls function b(), you can imaging a() allocates some memory in the call stack and so does b(). While b() is being executed, the memory allocated by a() stays as is. When b() returns and now a() continues its execution, it could allocate some more memory on the stack. When a function (any function) finishes, its memory is freed.
In call stacks each function's data should be seen as a separate stack element. Those elements are called stack frames. In your example you have 3 variables but only 1 element.
Also note that stack frames have variable size. For example, one frame might have 7 variables and take 48 bytes, while another might only have 1 variable, which allocates 100 KB (imagine a big array, for instance).
The memory in a given stack frame also resembles a stack in a way, in that you can't just insert or pop out a variable from the middle just like that, as that would require other variables' memory addresses to shift. However, you can read or write data from/to the entire stack frame. Even in a relatively low-level language like Rust, many of those thing are somewhat hidden from you, but it's definitely helpful if you understand how things work underneath.
Every function needs a specific amount of RAM to hold all variables in this function. These are called frames. Frames are stacked, not the variables in them.
Every time you call a function, a new frame is allocated. Every time you leave a function, the frame for this function is dropped.
The Function-Stack has nothing to do with accessing variables in the same frame.

Point a pointer to local variable created within a function

Here is the code:
var timePointer *[]time.Time
func UpdateHolidayList() error {
//updating logic: pulling list of holidays from API
holidaySlice := make([]time.Time, 0)
//appending some holidays of type time.Time to holidaySlice
//now holidaySlice contains a few time.Time values
timePointer = &holidaySlice
return nil
}
func main() {
//run UpdateHoliday every 7 days
go func() {
for {
UpdateHoliday()
time.Sleep(7 * 3600 * time.Hour)
}
}()
}
I have 4 questions:
holidaySlice is a local variable, is it safe to point a (global) pointer to it?
Is this whole code multi-thread safe?
After pointing timePointer to holidaySlice, can I access the values via timePointer
(If answer to 3. is "yes") The holiday list is constantly changing, so holidaySlice will be different each update. Will the values accessed via timePointer change accordingly then?
holidaySlice is a local variable allocated on the heap. Any variable pointing to the same heap location can access the data structure at that location. Whether or not it is safe depends on how you access it. Even if holidaySlice was not explicitly allocated on the heap, once you make a global variable point to it, Go compiler would detect that it "escapes", so it would allocate that on the heap.
The code is not thread-safe. You are modifying a shared variable (the global variable) without any explicit synchronization, so there is no guarantee on when or if other goroutines will see the updates to that variable.
If you update the contents of the timePointer array without explicit synchronization, there is no guarantee on when or if other goroutines will see those updates. You have to use synchronization primitives like sync/Mutex to delimit read/write access to data structures that can be updated/read by multiple goroutines.
As long as your main does not end timePointer will have access to the pointer that holidaySlice creates - since its heap allocated, compiler will detect its escape and not free the memory.
Nope, absolutely not. Look at the sync package
Yes you can. Just remember to iterate using *timePointer instead of timePointer
It will change - but not accordingly. Since you have not done any synchronization - you have no defined way of knowing what data is stored at slice pointed to by timePointer when it is read.

Can a simple write to one variable modify the state of another?

Taken from Operating Systems - Principles and Practices Vol 3. Chapter 5 Question 4. This isn't homework, I'm now curious.
Suppose that you mistakenly create an automatic (local) variable v in one
thread t1 and pass a pointer to v to another thread t2. Is it possible that a
write by t1 to some variable other than v will change the state of v as
observed by t2? If so, explain how this can happen and give an example. If
not, explain why not.
I don't think this is possible unless T1 declared v within a lower level scope, which was popped off the stack via modification to the afore-mentioned unrelated variable, followed by a swift reallocation of that memory to a new variable. But what I've described isn't a simple write, and that new variable isn't v anymore; at least not to T1.
Were this possible, I think it would imply that a simple write to any unrelated variable could somehow change another memory location even in single threaded programs.
So is this possible?

Implementing reference counting in a stack-based approach in C

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.

Variable inside for loop not re-initialized on each iteration?

I have a C++/CLI project that declares a String^ variable inside a for loop but does not initialize it. On the first iteration, the variable is set to some value. On each subsequent
iteration, it appears to be retaining the previous value. Shouldn't a variable in local scope be initialized to null (or equivalent) each time thru the loop? This happens with an int as well. Also, the compiler
does not warn of a potentially uninitialized value unless I set the warning level to W4, and even then it only warns for the int and not the String^.
This is sample code that shows the behavior.
#include "stdafx.h"
using namespace System;
int main(array<System::String ^> ^args)
{
for(int n = 0; n < 10; n++)
{
String^ variable;
int x;
switch(n)
{
case 1:
variable = "One";
x = 1;
break;
case 5:
variable = "Five";
x = 5;
break;
}
Console::WriteLine("{0}{1}", variable, x);
}
}
The output of this will be
One, 1
One, 1
One, 1
One, 1
Five, 5
Five, 5
Five, 5
Five, 5
Five, 5
Am I completely misunderstanding how locally scoped variables are supposed to be initialized? Is this a "feature" unique to managed C++? If I convert
this to C# the compiler will warn about both variables, even at the base warning level.
Disclaimer: I know C and C++ pretty well; C++/CLI, not so much. But the behavior you're seeing is essentially the same that I'd expect for a similar program in C or C++.
String^ is a handle to a String, similar to a pointer in C or C++.
Unless C++/CLI adds new rules for initialization of handles, a block-scope variable of type String^ with no explicit initialization will initially have a garbage value, consisting of whatever happened to be in that chunk of memory.
Each iteration of the loop conceptually creates and destroys any variables defined between the { and }. And each iteration probably allocates its local variables in the same memory location (this isn't required, but there's no real reason for it not to do so). The compiler could even generate code that allocates the memory on entry to the function.
So on the first iteration of your loop, variable is set to "One" (or rather, to a handle that refers to "One"), that's the value printed by Console::WriteLine. No problem there.
On the second iteration, variable is allocated in the same memory location that was used for it on the first iteration. No new value is assigned to it, so it retains the value that was stored in that memory location on the first iteration. The same thing happens with x.
You cannot count on the previous value being retained, and your program's behavior is undefined. If your goal were to write a correctly working program, rather than to understand how this incorrect program behaves, the solution would be to ensure that all your variables are properly initialized before they're used.
If you did the initial assignment on the second iteration rather than the first, the program would likely crash on the first iteration -- though even that's not guaranteed.
As for why the compiler doesn't warn about this, I don't know. I hesitate to suggest a compiler bug, but this could be one.
Also, even with high warning levels enabled, warning about uninitialized variables requires control flow analysis that may not be done by default. Enabling both warnings and a high level of optimization might give the compiler enough information to warn about both variable and x.
It still seems odd that it warns about x and not about variable with W4.
C++/CLI is only an extension/superset of standard C++ so it complies with most of its specifications, extending it only to fit with the CLI (~.Net) requirements.
Shouldn't a variable in local scope be initialized to null (or
equivalent) each time thru the loop?
AFAIK the C++ standard does not define the way local loop variables should be initialized.
So, to avoid any overhead, compilers usually don't use specific local memory management for loops : see this SO question :
Is there any overhead to declaring a variable within a loop? (C++)
Am I completely misunderstanding how locally scoped variables are supposed to be initialized?
Is this a "feature" unique to managed C++
So no, this is not a feature or special behavior : your C++/CLI compiler is only using standard C++ practices.
If I convert this to C# the compiler will warn about both variables,
even at the base warning level.
C# and AFAIK Java try hard to avoid any undefined behaviors, so they force you to initialize local variables before they are used.
Here is the CIL resulting from the compilation (I've done some formatting and commenting to make this bunch of text understandable :)) :
.locals init (int32 V_0, int32 V_1, string V_2, int32 V_3)
// ^ ^ ^ ^
// n x variable tmp
// initialization of "n"
IL_0000: ldc.i4.0
IL_0001: stloc.0
IL_0002: br.s IL_0008
// loop starts here
// post iteration processing
IL_0004: ldloc.0
IL_0005: ldc.i4.1
IL_0006: add
IL_0007: stloc.0
// stop condition check
IL_0008: ldloc.0
IL_0009: ldc.i4.s 10
IL_000b: bge.s IL_003e
// initialization of temporary "tmp" variable for switch
IL_000d: ldloc.0
IL_000e: stloc.3
// check if "tmp" is 3
IL_000f: ldloc.3
IL_0010: ldc.i4.1
// if so go to "variable" intialization
IL_0011: beq.s IL_0019
// check if "tmp" is 5
IL_0013: ldloc.3
IL_0014: ldc.i4.5
IL_0015: beq.s IL_0023
// go to display
IL_0017: br.s IL_002b
// initialization of "variable"
IL_0019: ldstr "One"
IL_001e: stloc.2
...
So the variable is indeed never implicitly manipulated by the code generated by the compiler.

Resources