destroying a class instance doesn't kill instances it owns in vala - linux

I have a subclass of Gtk.Box that contains a GLib.Timer that fires a notification after a given interval. I have method in this class that calls this.destroy() on the Gtk.Box. The timer continues to run and fires the notification even after its parent instance has been destroyed. All instances of this class that have been destroyed exhibit this behaviour and continue to use CPU and memory untill the process is killed.
How do I fix this? How can I kill instances effectively and how do I manually free memory instead of relying on vala's garbage collection.
edit: here's an (embarrassing) mvce
// mvce_deletable
// nine
// 2017.01.11
// valac --pkg gtk+-3.0 --pkg glib-2.0 deletablebox.vala
using Gtk;
using GLib;
class RemovableBox : Gtk.Box {
private Gtk.Button delete_button;
private GLib.Timer timer;
private Gtk.Label label;
public RemovableBox () {
delete_button = new Gtk.Button.with_label ("DESTROY");
delete_button.clicked.connect (()=>{this.destroy();});
this.add (delete_button);
label = new Gtk.Label ("0000000");
this.add (label);
timer = new GLib.Timer ();
timer.start ();
Timeout.add (50, update);
this.show_all ();
}
private bool update () {
if (timer.elapsed () > 10.0f) {
stdout.printf("and yet it breathes\n");
}
label.set_text ("%f".printf(timer.elapsed()));
return true;
}
}
int main ( string [] args ) {
Gtk.init(ref args);
var window = new Gtk.Window ();
window.destroy.connect (Gtk.main_quit);
var delete_me = new RemovableBox ();
window.add ( delete_me );
window.show_all();
Gtk.main();
return 0;
}
I added a timer_id the the RemovableBox class but it still doesn't work as desired.
class RemovableBox : Gtk.Box {
private Gtk.Button delete_button;
private uint timeout_id;
private GLib.Timer timer;
private Gtk.Label label;
public RemovableBox () {
delete_button = new Gtk.Button.with_label ("DESTROY");
delete_button.clicked.connect (()=>{this.destroy();});
this.add (delete_button);
label = new Gtk.Label ("0000000");
this.add (label);
timer = new GLib.Timer ();
timer.start ();
timeout_id = Timeout.add (40, update);
this.show_all ();
}
~ RemovableBox () {
Source.remove (timeout_id);
}
private bool update () {
if (timer.elapsed () > 10.0f) {
stdout.printf("and yet it breathes\n");
}
label.set_text ("%f".printf(timer.elapsed()));
return true;
}
}

GLib.Timer is a stopwatch that returns the elapsed time. It doesn't generate events, but GLib.Timeout does.
GLib makes use of an event loop. This is the same for GTK+, which uses the same underlying GLib event loop. GLib.Timeout is used to create one kind of event source - a timer that fires after a given interval. When your program creates the event source you are given an identifier for the source. For example:
timer_id = Timeout.add_seconds (1, my_callback_function);
What your program needs to do is store that timer identifier in the object and then when the button click handler is called you can remove the timer as a source of events:
Source.remove (timer_id);
Strictly speaking Vala doesn't have a garbage collection cycle. Other languages will collect references that are no longer used and then remove the resources allocated to them during a clean up cycle. Vala uses reference counting, but it is deterministic. So when an object is no longer used, usually when it goes out of scope, the resources allocated to the object are removed immediately. For normal objects in Vala, rather than compact classes, a destructor is also called when the object is freed. This allows for the resource allocation is initialization (RAII) pattern to be used effectively in Vala.
In general you should not be manually freeing objects, Vala's reference counting is very good. I think it is important to understand GLib's event loop and sources of events to understand what is going on. For a detailed description see GLib's documentation on its main event loop.
Now you have provided a MCVE we can look in detail at how Vala manages memory. If you want to dig deep into what is going on behind the scenes you can use the --ccode switch with valac.
The first line of interest in your program is:
Timeout.add (50, update);
Looking at the C code from valac this line uses the g-timeout-add-full () function:
g_timeout_add_full (G_PRIORITY_DEFAULT, (guint) 50, _removable_box_update_gsource_func, g_object_ref (self), g_object_unref);
The crucial part here is g_object_ref (self). This increases the reference count for the object by one and passes a pointer to the object. This makes a lot of sense, because the update () callback passed in the Vala code makes use of the instance data from the object. Vala is doing the right thing and making sure that the instance data is kept alive while the timer is around. The 'g_object_unref' is called when the source is removed. Here's a modified version of your program putting this understanding in to practise:
// mvce_deletable
// nine
// 2017.01.11
// valac --pkg gtk+-3.0 deletablebox.vala
using Gtk;
class RemovableBox : Gtk.Box {
private Gtk.Button delete_button;
private uint timeout_id;
private GLib.Timer timer;
private Gtk.Label label;
public RemovableBox () {
delete_button = new Gtk.Button.with_label ("DESTROY");
delete_button.clicked.connect (()=>{this.tidy_up_and_destroy ();});
this.add (delete_button);
label = new Gtk.Label ("0000000");
this.add (label);
timer = new GLib.Timer ();
timer.start ();
timeout_id = Timeout.add (40, update);
this.show_all ();
}
~ RemovableBox () {
print ("RemovableBox destructor called\n");
}
private bool update () {
if (timer.elapsed () > 10.0f) {
stdout.printf("and yet it breathes\n");
}
label.set_text ("%f".printf(timer.elapsed()));
return true;
}
private void tidy_up_and_destroy () {
print ("RemovableBox.tidy_up_and_destroy called\n");
Source.remove (timeout_id);
this.destroy ();
}
}
void main ( string [] args ) {
Gtk.init(ref args);
var window = new Gtk.Window ();
window.window_position = WindowPosition.CENTER;
window.resize (250,50);
window.destroy.connect (Gtk.main_quit);
window.add (new RemovableBox ());
window.show_all();
Gtk.main();
}
Previously the program still kept a reference to the RemovableBox object and so was never completely removed. By removing the event source first then calling this.destroy (); it means there are no more references and the object is removed.
There is one other important point here. The lines:
var delete_me = new RemovableBox ();
window.add ( delete_me );
in main () have been changed to:
window.add (new RemovableBox ());
Vala objects exist for the scope of the block they were created in. By assigning the object to delete_me you are keeping a reference to the object for the rest of the main () block. By changing that to be an argument of a method call it is just for the call and so is freed when the button is clicked.
By the way, GLib is automatically included when using valac so there is no need for using GLib; or compiling with --pkg glib-2.0.

You are confusing automatic reference couting with full garbage collection.
There is no garbage collector in GLib, but classes have a reference count instead that is increased when a GObject is used in multiple places and decreased when it is no longer used in those places, until it reaches zero. The object is then freed.
In fact in C code the reference counting is manual:
// Reference count is set to 1 on gobject_new
gpointer obj = gobject_new (G_SOME_OBJECT, NULL);
// It can be manually increased when the object is stored in multiple places
// Let's increase the ref count to 2 here
gobject_ref (obj);
// Let's decrease it until it reaches 0
gobject_unref (obj);
gobject_unref (obj);
// Here the object is destroyed (but the pointer is still pointing to the previous memory location, e.g. it is a dangling pointer)
// gobject_clear (obj) can be used in situation where the variable is reused
// It still only unrefs the object by 1 though! In addition it will set obj to NULL
Vala adds the auto to reference counting, which makes it "automatic reference counting" (ARC). That is you don't have to worry about the reference count in Vala, it will add the appropriate ref and unref operations for you.
In full garbage collection (like in C#, Java, ...) memory deallocation is not deterministic, the object can be kept alive even if it isn't used anymore. This is done using something called a "managed heap" and a garbage collector is run in the background (i.e. as a GC thread).
Now that we have the background stuff covered to your actual problem:
You have to remove the Gtk.Box from it's parent container and also set any references you might still have in your Vala code to null in order to get the reference count to 0. It will then be unrefed.
There are of course other options, like disabling the timer, etc. You should really add an MVCE to your question for us to be able to give you some design advice on your code.
PS: Reference counting is often considered as a simple method of garbage collection. That's why I write full garbage collection (also called tracing garbage collection) in order to not confuse the two terms. See the Wikipedia article on garbage collection.

Related

Vala Threading: invocation of void method not allowed as expression

Hey i've been writing an application in which i need to create thread to perform background tasks while the GUI is being loaded. However no matter that i do i can find a way around this error:
error: invocation of void method not allowed as expression
Thread<void> thread = new Thread<void>.try("Conntections Thread.", devices_online(listmodel));
The line in question is the creating of a new thread which calls the "devices_online" method.
The Full code which is being effected is:
try {
Thread<void> thread = new Thread<void>.try("Conntections Thread.", devices_online(listmodel));
}catch(Error thread_error){
//console print thread error message
stdout.printf("%s", thread_error.message);
}
And Method is:
private void devices_online(Gtk.ListStore listmodel){
//clear the listview
listmodel.clear();
//list of devices returned after connection check
string[] devices = list_devices();
//loop through the devices getting the data and adding the device
//to the listview GUI
foreach (var device in devices) {
string name = get_data("name", device);
string ping = get_data("ping", device);
listmodel.append (out iter);
listmodel.set (iter, 0, name, 1, device, 2, ping);
}
}
Ive done so much Googleing but Vala isn't exactly the most popular language. Any help?
Like the compiler error says, you are getting a void by calling a method. Then you are trying to pass the void value into the thread constructor.
Thread<void> thread = new Thread<void>
.try ("Conntections Thread.", devices_online (listmodel));
The second cunstructor argument of Thread<T>.try () expects a delagate of type ThreadFunc<T> which you are not satisfying.
You are confusing a method call with a method delegate.
You can pass an anonymous function to fix that:
Thread<void> thread = new Thread<void>
.try ("Conntections Thread.", () => { devices_online (listmodel); });

How can one update GTK+ UI in Vala from a long operation without blocking the UI

When I use any of the codes in this page without modifying anything: https://wiki.gnome.org/Projects/Vala/AsyncSamples
I always get:
warning: ‘g_simple_async_result_new’ is deprecated: Use 'g_task_new' instead.
So I proceed with the recommendation of using GTask. However, when I try to use GLib.Task in Vala, I get stuck just declaring a task. So instead of using async from GIO in my own code, since it is deprecated, I try to use GLib.Task to simply update the label of a Gtk Button with numbers from a for loop, such that the code looks like this:
using Gtk;
Button button;
public static int main (string[] args) {
Gtk.init (ref args);
var window = new Window ();
window.title = "Count without blocking the UI";
window.border_width = 10;
window.window_position = WindowPosition.CENTER;
window.set_default_size (350, 70);
window.destroy.connect (Gtk.main_quit);
button = new Button.with_label ("Start counting");
button.clicked.connect (() => {
GLib.Task task = new GLib.Task(button, new Cancellable());
});
window.add (button);
window.show_all ();
Gtk.main ();
return 0;
}
void count(){
for(int i = 0; i < 10000; i++){
button.label = i.to_string();
}
}
But when compiling I get: error: ‘_data_’ undeclared (first use in this function)
_tmp3_ = g_task_new_finish (_data_->_res_);
The line number 15 is what is causing the compiler to throw that error. It comes from the C code that the vala compiler generates.
The main problem I found is that the GTask constructor signatures in Vala are different from C. Therefore, I could not re-create the code found here: GUI becomes unresponsive after clicking the button using GTK+ in C
Because for starters, I am not allowed to pass more than two arguments to the GLib.Task object constructor. The constructors of the Task object are different in each language. The constructor for GLib.Task in Vala can be found here.
Hence my question:
Are there any examples on how to use GLib Task (GTask) in Vala to perform an operation that updates the UI without blocking it? If not, is there another way to update the UI without blocking it? A way that is not deprecated?
Thanks.
P.S: I have already tried GLib.Thread, GLib.ThreadPool, and GLib.Idle. They all block the UI while in the for loop. GLib.Idle does not block the UI completely, but it renders it buggy in the sense that it becomes really slow to respond to user input while the loop is running.
It's perfectly fine to use async and there's some work already for porting the current code to use GTask.
Your counting code is blocking, so even if its execution is cushioned with GTask, it will still block the UI.
The correct way of performing CPU intensive background operations either use subprocess asynchronously or launch the work in a thread and dispatch in the main loop.
async void test_async () {
new Thread<void> (() => {
// count here...
test_async.callback ();
});
yield;
}
The GTask or more generally GAsyncResult only provide a container for holding the result of an asynchronous operation. They also recommend to use a GThreadPool, but it's a bit more boilerplate.
Another interesting thing is that test_async.callback is actually a SourceFunc, so you can pass it around in GLib.Timeout.
EDIT:
To fit more your question, if you want to update the UI while it progress, use an async loop:
async test_callback () {
for (var i = 0; i < 10000; i++) {
button.label = i.to_string ();
Idle.add (test_async.callback);
yield; // pause execution until retriggered in idle
}
}
Here's a full and working example:
using Gtk;
Button button;
public static int main (string[] args) {
Gtk.init (ref args);
var window = new Window ();
window.title = "Count without blocking the UI";
window.border_width = 10;
window.window_position = WindowPosition.CENTER;
window.set_default_size (350, 70);
window.destroy.connect (Gtk.main_quit);
button = new Button.with_label ("Start counting");
button.clicked.connect (() => {
count ();
});
window.add (button);
window.show_all ();
Gtk.main ();
return 0;
}
async void count(){
for(int i = 0; i < 10000; i++){
button.label = i.to_string();
Idle.add (count.callback);
yield;
}
}

Getting information from background thread onto the main thread for Swift

I seem to be having some trouble getting information from my global queue into my main queue.
override func viewDidLoad() {
super.viewDidLoad()
dispatch_async(dispatch_get_global_queue(Int(QOS_CLASS_USER_INTERACTIVE.value), 0)) {
let query = PFQuery(className: "VenueData")
query.whereKey("name", equalTo: "Sedona")
var object = query.findObjects()
if let myObject = object as? [PFObject] {
for x in myObject {
var hotelLabel: String? = x.objectForKey("name") as? String
println(hotelLabel)
dispatch_async(dispatch_get_main_queue()) {
self.new = hotelLabel!
self.hotelarray.append(self.new)
}
}
}
}
}
Even though I am able to change the UI through the dispatch_get_main_queue, I seem to be having trouble updating other stuff (e.g. Array) and if I were to println(hotelarray) outside of the block, only an empty array would show up.
I understand this happens because the mainthread loads significantly faster than the time it takes for the background thread to load its data. What is puzzles me is that it seems as if the hotel array will not get updated regardless of what happens inside the dispatch_get_main_queue whereas UI related stuff such as UILabels updates accordingly. Any help is greatly appreciated.

Set DocumentViewer.Document from another thread?

I have a class with a method (CreateDocument) that fires an event at the end. The event args contain a FixedDocument. In my MainWindow code I try to set a DocumentViewer's Document like:
void lpage_DocCreated(object sender, LabelDocumentEventArgs e)
{
this.Dispatcher.BeginInvoke(DispatcherPriority.Normal,
new DispatcherOperationCallback(delegate
{
FixedDocument fd = e.doc;
documentViewer1.Document = fd;
documentViewer1.FitToWidth();
return null;
}), null);
}
I receive "The calling thread cannot access this object because a different thread owns it." on line documentViewer1.Document = fd;
I am able to update a progress bar in another event handler that the same method fires while it is working:
Int32 progress = Int32.Parse(sender.ToString());
progBar.Dispatcher.BeginInvoke(System.Windows.Threading.DispatcherPriority.Normal,
new DispatcherOperationCallback(delegate
{
progBar.Value = progress;
return null;
}), null);
I can't figure out why I can't set the document when I'm essentially doing the same type of thing when I set the progress bar value.
The FixedDocument element also has thread-affinity. So if you are creating it in a separate thread than the documentViewer1, then you would get that exception.
Basically, anything that derives from DispatcherObject has a thread-affinity. FixedDocument derives from DispatcherObject, just like the viewer controls.

How do I block access to a method until animations are complete

I have a Silverlight app. that has a basic animation where a rectangle is animated to a new position. The animation consists of two DoubleAnimation() - one transforms the X, the other transforms the Y. It works OK.
I basically want to block any other calls to this animate method until the first two animations have completed. I see that the DoubleAnimation() class has a Completed event it fires but I haven't been successful in constructing any kind of code that successfully blocks until both have completed.
I attempted to use Monitor.Enter on a private member when entering the method, then releasing the lock from one of the animations Completed event, but my attempts at chaining the two events (so the lock isn't released until both have completed) haven't been successful.
Here's what the animation method looks like:
public void AnimateRectangle(Rectangle rect, double newX, double newY)
{
var xIsComplete = false;
Duration duration = new Duration(new TimeSpan(0, 0, 0, 1, 350));
var easing = new ElasticEase() { EasingMode = EasingMode.EaseOut, Oscillations = 1, Springiness = 4 };
var animateX = new DoubleAnimation();
var animateY = new DoubleAnimation();
animateX.EasingFunction = easing;
animateX.Duration = duration;
animateY.EasingFunction = easing;
animateY.Duration = duration;
var sb = new Storyboard();
sb.Duration = duration;
sb.Children.Add(animateX);
sb.Children.Add(animateY);
Storyboard.SetTarget(animateX, rect);
Storyboard.SetTargetProperty(animateX, new PropertyPath("(Canvas.Left)"));
Storyboard.SetTarget(animateY, rect);
Storyboard.SetTargetProperty(animateY, new PropertyPath("(Canvas.Top)"));
animateX.To = newX;
animateY.To = newY;
sb.Begin();
}
EDIT (added more info)
I ran into this initially because I was calling this method from another method (as it processed items it made a call to the animation). I noticed that the items didn't end up where I expected them to. The new X/Y coordinates I pass in are based on the items current location, so if it was called multiple times before it finished, it ended up in the wrong location. As a test I added a button that only ran the animation once. It worked. However, if I click on the button a bunch of times in a row I see the same behavior as before: items end up in the wrong location.
Yes, it appears Silverlight animations are run on the main UI thread. One of the tests I tried I added two properties that flagged whether both animations had completed yet. In the AnimateRectange() method I checked them inside of a while loop (calling Thread.Sleep). This loop never completed (so it's definitely on the same thread).
So I created a queue to process the animations in order:
private void ProcessAnimationQueue()
{
var items = this.m_animationQueue.GetEnumerator();
while (items.MoveNext())
{
while (this.m_isXanimationInProgress || this.m_isYanimationInProgress)
{
System.Threading.Thread.Sleep(100);
}
var item = items.Current;
Dispatcher.BeginInvoke(() => this.AnimateRectangle(item.Rect.Rect, item.X, item.Y));
}
}
Then I call my initial routine (which queues up the animations) and call this method on a new thread. I see the same results.
As far as I am aware all of the animations in Silverlight are happening on the UI thread anyway. I am guessing that only the UI thread is calling this animation function anyway, so I am not sure that using locking will help. Do you really want to be blocking the entire thread or just preventing another animation from starting?
I would suggest something more like this:
private bool isAnimating = false;
public void AnimateRectangle(Rectangle rect, double newX, double newY)
{
if (isAnimating)
return;
// rest of animation code
sb.Completed += (sender, e) =>
{
isAnimating = false;
};
isAnimating = true;
sb.Begin();
}
Just keep track of whether or not you are currently animating with a flag and return early if you are. If you don't want to lose potential animations your other option is to keep some kind of a queue for animation which you could check/start when each animation has completed.
This question really peaked my interest. In fact I'm going to include it in my next blog post.
Boiling it down, just to be sure we are talking about the same thing, fundementally you don't want to block the call to AnimateRectangle you just want to "queue" the call so that once any outstanding call has completed its animation this "queued" call gets executed. By extension you may need to queue several calls if a previous call hasn't even started yet.
So we need two things:-
A means to treat what are essentially asynchronous operations (sb.Begin to Completed event) as a sequential operation, one operation only starting when the previous has completed.
A means to queue additional operations when one or more operations are yet to complete.
AsyncOperationService
Item 1 comes up in a zillion different ways in Silverlight due to the asynchronous nature of so many things. I solve this issue with a simple asynchronous operation runner blogged here. Add the AsyncOperationService code to your project.
AsyncOperationQueue
Its item 2 that really took my interest. The variation here is that whilst an existing set of operations are in progress there is demand to add another. For a general case solution we'd need a thread-safe means of including another operation.
Here is the bare-bones of a AsyncOperationQueue:-
public class AsyncOperationQueue
{
readonly Queue<AsyncOperation> myQueue = new Queue<AsyncOperation>();
AsyncOperation myCurrentOp = null;
public void Enqueue(AsyncOperation op)
{
bool start = false;
lock (myQueue)
{
if (myCurrentOp != null)
{
myQueue.Enqueue(op);
}
else
{
myCurrentOp = op;
start = true;
}
}
if (start)
DequeueOps().Run(delegate { });
}
private AsyncOperation GetNextOperation()
{
lock (myQueue)
{
myCurrentOp = (myQueue.Count > 0) ? myQueue.Dequeue() : null;
return myCurrentOp;
}
}
private IEnumerable<AsyncOperation> DequeueOps()
{
AsyncOperation nextOp = myCurrentOp;
while (nextOp != null)
{
yield return nextOp;
nextOp = GetNextOperation();
}
}
}
Putting it to use
First thing to do is convert your existing AnimateRectangle method into a GetAnimateRectangleOp that returns a AsyncOperation. Like this:-
public AsyncOperation GetAnimateRectangleOp(Rectangle rect, double newX, double newY)
{
return (completed) =>
{
// Code identical to the body of your original AnimateRectangle method.
sb.Begin();
sb.Completed += (s, args) => completed(null);
};
}
We need to hold an instance of the AsyncOperationQueue:-
private AsyncOperationQueue myAnimationQueue = new AsyncOperationQueue();
Finally we need to re-create AnimateRectangle that enqueues the operation to the queue:-
public void AnimateRectangle(Rectangle rect, double newX, double newY)
{
myAnimationQueue.Enqueue(GetAnimateRectangleOp(rect, newX, newY)
}

Resources