commit 3e5c0e1a4885157c9fc74c521a4459b7ba908525
Author: Zach Goldberg <zgold@bluesata.(none)>
Date:   Thu Feb 11 16:49:13 2010 -0500

    Implement 4 types of callback scope: call, async, notify and infinity.

diff --git a/gi/pygi-argument.c b/gi/pygi-argument.c
index f0373cb..91d8609 100644
--- a/gi/pygi-argument.c
+++ b/gi/pygi-argument.c
@@ -624,11 +624,15 @@ _pygi_argument_to_array (GArgument  *arg,
 GArgument
 _pygi_argument_from_object (PyObject   *object,
                             GITypeInfo *type_info,
-                            GITransfer  transfer)
+                            GITransfer  transfer,
+			    GIArgInfo  *arg_info)
 {
     GArgument arg;
     GITypeTag type_tag;
 
+    GSList *call_free_list = NULL; /* list of PyGiCallbackInvokeInfo* */
+    GSList *data_for_notify = NULL; /* list of PyGiCallbackInfo* */
+
     type_tag = g_type_info_get_tag(type_info);
 
     switch (type_tag) {
@@ -842,7 +846,7 @@ _pygi_argument_from_object (PyObject   *object,
                     goto array_item_error;
                 }
 
-                item = _pygi_argument_from_object(py_item, item_type_info, item_transfer);
+                item = _pygi_argument_from_object(py_item, item_type_info, item_transfer, arg_info);
 
                 Py_DECREF(py_item);
 
@@ -880,7 +884,11 @@ array_item_error:
                 case GI_INFO_TYPE_CALLBACK:
 		    {						
 			arg.v_pointer = _pygi_make_native_closure((GICallableInfo*)info,
-								  object);
+						       arg_info,
+						       object,
+						       &call_free_list,
+						       &destroy_notify_infos);
+	
 			break;
 		    }
                 case GI_INFO_TYPE_BOXED:
@@ -1008,7 +1016,7 @@ array_item_error:
                     goto list_item_error;
                 }
 
-                item = _pygi_argument_from_object(py_item, item_type_info, item_transfer);
+                item = _pygi_argument_from_object(py_item, item_type_info, item_transfer, arg_info);
 
                 Py_DECREF(py_item);
 
@@ -1107,12 +1115,12 @@ list_item_error:
                 py_key = PyList_GET_ITEM(keys, i);
                 py_value = PyList_GET_ITEM(values, i);
 
-                key = _pygi_argument_from_object(py_key, key_type_info, item_transfer);
+                key = _pygi_argument_from_object(py_key, key_type_info, item_transfer, arg_info);
                 if (PyErr_Occurred()) {
                     goto hash_table_item_error;
                 }
 
-                value = _pygi_argument_from_object(py_value, value_type_info, item_transfer);
+                value = _pygi_argument_from_object(py_value, value_type_info, item_transfer, arg_info);
                 if (PyErr_Occurred()) {
                     _pygi_argument_release(&key, type_info, GI_TRANSFER_NOTHING, GI_DIRECTION_IN);
                     goto hash_table_item_error;
diff --git a/gi/pygi-argument.h b/gi/pygi-argument.h
index c162f2f..0ed859b 100644
--- a/gi/pygi-argument.h
+++ b/gi/pygi-argument.h
@@ -31,6 +31,10 @@ G_BEGIN_DECLS
 
 /* Private */
 
+GSList* call_free_list;
+GSList *destroy_notify_infos;
+gint cur_arg_index;
+
 gint _pygi_g_type_info_check_object (GITypeInfo *type_info,
                                      PyObject   *object);
 
@@ -46,7 +50,8 @@ GArray* _pygi_argument_to_array (GArgument  *arg,
 
 GArgument _pygi_argument_from_object (PyObject   *object,
                                       GITypeInfo *type_info,
-                                      GITransfer  transfer);
+                                      GITransfer  transfer,
+				      GIArgInfo  *arg_info);
 
 PyObject* _pygi_argument_to_object (GArgument  *arg,
                                     GITypeInfo *type_info,
diff --git a/gi/pygi-closure.c b/gi/pygi-closure.c
index d7da472..b3720ce 100644
--- a/gi/pygi-closure.c
+++ b/gi/pygi-closure.c
@@ -23,12 +23,12 @@
 
 #include <string.h>
 #include <time.h>
+#include <sys/mman.h>
 
 #include <datetime.h>
 #include <pygobject.h>
 
-void _pygi_handle_closure(ffi_cif *cif, void *result, void **args, void *userdata)
-{
+void _pygi_handle_closure(ffi_cif *cif, void *result, void **args, void *userdata){
     PyGILState_STATE gstate;
     PyGINativeClosure *closure = userdata;
     gint n_args;
@@ -60,10 +60,11 @@ void _pygi_handle_closure(ffi_cif *cif, void *result, void **args, void *userdat
 
     return_tag = g_type_info_get_tag (return_type);
     return_transfer = g_callable_info_get_caller_owns(closure->info);
-  
+
     pyargs = PyTuple_New(n_args);
 
     if (pyargs == NULL) {
+	PyErr_Clear();
         goto end;
     }
 
@@ -72,33 +73,173 @@ void _pygi_handle_closure(ffi_cif *cif, void *result, void **args, void *userdat
 
 end:
     PyGILState_Release (gstate);
+
+    /* If this was an async type closure we need to free it now that we have used it
+     */
+    if (closure->free_on_use)
+	_pygi_callback_invoke_closure_free(closure);
+
+}
+
+void _pygi_callback_notify_info_free(gpointer data)
+{
+
+    GSList* info;
+    for(info = data; info; info->next)
+	g_slice_free(PyGICallbackNotifyInfo, info->data);
+
+}
+void _pygi_callback_invoke_closure_free(gpointer data)
+{
+    PyGINativeClosure* invoke_closure = (PyGINativeClosure *)data;
+    
+    if (invoke_closure->info)
+	g_base_info_unref((GIBaseInfo*)invoke_closure->info);
+
+    munmap(invoke_closure->closure, sizeof(ffi_closure));
+    g_slice_free(ffi_cif, invoke_closure->cif);
+
+    if (!invoke_closure->is_destroy_notify)
+	Py_DECREF(invoke_closure->function);
+
+    g_slice_free(PyGINativeClosure, invoke_closure);
+}
+
+static void
+_pygi_destroy_notify_callback_closure(ffi_cif *cif,
+				      void *result,
+				      void **args,
+				      void *data)
+{
+    PyGINativeClosure *info = (PyGINativeClosure *)data;
+
+    _pygi_callback_invoke_closure_free(info->notify);
+    _pygi_callback_invoke_closure_free(info);    
+}
+
+
+static gboolean
+_pygi_destroy_notify_create(PyGINativeClosure* destroy_notify)
+{
+    static const ffi_type *destroy_notify_args[] = {&ffi_type_pointer};
+    ffi_status status;
+
+    g_assert(destroy_notify);
+
+    destroy_notify->is_destroy_notify = TRUE;
+
+    destroy_notify->closure = mmap(NULL, sizeof (ffi_closure),
+                         PROT_EXEC | PROT_READ | PROT_WRITE,
+                         MAP_ANON | MAP_PRIVATE, -1, sysconf (_SC_PAGE_SIZE));
+
+    if (!destroy_notify->closure) {
+        PyErr_SetString(PyExc_MemoryError, 
+			"mmap failed");
+        return FALSE;
+    }
+
+    destroy_notify->cif = g_slice_new(ffi_cif);
+
+    status = ffi_prep_cif(destroy_notify->cif, FFI_DEFAULT_ABI,
+                          1, &ffi_type_void,
+                          (ffi_type**)destroy_notify_args);
+    
+    if (status != FFI_OK) {
+        PyErr_SetString(PyExc_MemoryError, 
+			"ffi_prep_cif failed");
+        munmap(destroy_notify->closure, sizeof (ffi_closure));
+        return FALSE;
+    }
+
+    status = ffi_prep_closure(destroy_notify->closure, destroy_notify->cif,
+                              _pygi_destroy_notify_callback_closure, destroy_notify);
+
+    if (status != FFI_OK) {
+        PyErr_SetString(PyExc_MemoryError, 
+			"ffi_prep_closurefailed");
+        munmap(destroy_notify->closure, sizeof (ffi_closure));
+        return FALSE;
+    }
+
+    if (mprotect(destroy_notify->closure, sizeof (ffi_closure), PROT_READ | PROT_EXEC) == -1) {
+        PyErr_SetString(PyExc_MemoryError, 
+			"mprotect ffi_closure failed");
+        munmap(destroy_notify->closure, sizeof (ffi_closure));
+        return FALSE;
+    }
+
+    return TRUE;
 }
 
 
 gpointer
 _pygi_make_native_closure (GICallableInfo* info,
-			   PyObject *function)
+			   GIArgInfo* arg_info,
+			   PyObject *function, 
+			   GSList**  call_free_list,
+			   GSList** destroy_notify_infos)
 {
-    /* Code copied with pythonic modifications from seed's make_native_closure() method */
     ffi_cif *cif;
     ffi_closure *fficlosure;
     PyObject* cache;
     PyObject* dict;
     PyGINativeClosure *invoke_closure;
+    PyGICallbackNotifyInfo *notify_info;
+    GSList *l;
+    
+    for (l = *destroy_notify_infos; l; l->next){
+	PyGICallbackNotifyInfo* notify_info = l->data;
+	PyGINativeClosure* callback = notify_info->closure;
+	if (notify_info->destroy_arg_index != cur_arg_index)
+	    continue;
+      
+	invoke_closure = g_slice_new0(PyGINativeClosure);
+	invoke_closure->arg_index = cur_arg_index;
+	if(!_pygi_destroy_notify_create(invoke_closure)){
+	    g_slice_free(PyGINativeClosure, invoke_closure);
+	    return FALSE;
+	}      
+      
+	invoke_closure->notify = (struct PyGINativeClosure*)callback;
+	return invoke_closure;
+    }
 
     Py_INCREF(function);
 
+    /* Build the closure itself */
     cif = g_slice_new0(ffi_cif);
 
     invoke_closure = g_slice_new0(PyGINativeClosure);
     invoke_closure->info = (GICallableInfo *) g_base_info_ref ((GIBaseInfo *) info);  
     invoke_closure->function = function;
     invoke_closure->cif = cif;
-
+    invoke_closure->free_on_use = FALSE;
+    invoke_closure->arg_index = cur_arg_index;
+    
     fficlosure =
-        g_callable_info_prepare_closure (info, cif, _pygi_handle_closure,
+	g_callable_info_prepare_closure (info, cif, _pygi_handle_closure,
 					 invoke_closure);
     invoke_closure->closure = fficlosure;
-
-    return (gpointer) invoke_closure->closure;
+    
+    //Setup scope 
+    switch (g_arg_info_get_scope(arg_info)) {
+    case GI_SCOPE_TYPE_CALL:
+	/* Append this NativeClosure to a list of closure that we will free
+	   after we're done with this function invokation */
+	*call_free_list = g_slist_prepend(*call_free_list, invoke_closure);
+	break;
+    case GI_SCOPE_TYPE_NOTIFIED:
+	/* Record the index of this notify callback's destroy argument so
+	   we know it when we see it in future calls */
+	notify_info = g_slice_new0(PyGICallbackNotifyInfo);
+	notify_info->destroy_arg_index = g_arg_info_get_destroy(arg_info);
+	notify_info->closure = invoke_closure;
+	*destroy_notify_infos = g_slist_prepend(*destroy_notify_infos, notify_info);
+	
+	break;
+    case GI_SCOPE_TYPE_ASYNC:
+	invoke_closure->free_on_use = TRUE;		
+    }
+    
+    return invoke_closure->closure;
 }
diff --git a/gi/pygi-closure.h b/gi/pygi-closure.h
index da83325..83eaff2 100644
--- a/gi/pygi-closure.h
+++ b/gi/pygi-closure.h
@@ -31,19 +31,42 @@ G_BEGIN_DECLS
 
 typedef struct _PyGiNativeClosure
 {
+    /* Need to keep a reference to the
+       notify callback such that when this destroy callback is called
+       (assuming this callback is a destroynotify)
+       it knows a reference to the actual callback */
+    struct PyGINativeClosure* notify;
+
     GICallableInfo *info;
     PyObject *function;
     
     ffi_closure *closure;
     ffi_cif *cif;
+    gint arg_index;
+    gboolean free_on_use;
+    gboolean is_destroy_notify;
+    
 } PyGINativeClosure;
 
+typedef struct _PyGiCallbackNotifyInfo
+{
+    gint destroy_arg_index;
+    PyGINativeClosure *closure;
+} PyGICallbackNotifyInfo; // == GjsCallbackInfo
+
 
 
 void _pygi_handle_closure(ffi_cif *cif, void *result, void 
 			  **args, void *userdata);
 
+void _pygi_callback_invoke_closure_free(gpointer user_data);
+
+void _pygi_callback_notify_info_free(gpointer user_data);
+
 gpointer
 _pygi_make_native_closure (GICallableInfo* info,
-			   PyObject *function);
+			   GIArgInfo* arg_info,			   
+			   PyObject *function,
+			   GSList** call_free_list, 
+			   GSList** destroy_notify_infos);
 #endif
diff --git a/gi/pygi-info.c b/gi/pygi-info.c
index 18981a2..1999a53 100644
--- a/gi/pygi-info.c
+++ b/gi/pygi-info.c
@@ -547,6 +547,15 @@ _wrap_g_function_info_invoke (PyGIBaseInfo *self,
     args_is_auxiliary = g_newa(gboolean, n_args);
     memset(args_is_auxiliary, 0, sizeof(args_is_auxiliary) * n_args);
 
+
+    /* Initialize list of callbacks of scope 'call' that we will
+       free post function invokation */
+    call_free_list = NULL;
+
+    /* Initialize list of destroy indicies; metadata for callbacks
+       of scope 'notified' */
+    destroy_notify_infos = NULL;
+
     if (is_method) {
         /* The first argument is the instance. */
         n_in_args += 1;
@@ -827,7 +836,8 @@ _wrap_g_function_info_invoke (PyGIBaseInfo *self,
                 g_assert(py_args_pos < n_py_args);
                 py_arg = PyTuple_GET_ITEM(py_args, py_args_pos);
 
-                *args[i] = _pygi_argument_from_object(py_arg, arg_type_infos[i], transfer);
+		cur_arg_index = i;
+                *args[i] = _pygi_argument_from_object(py_arg, arg_type_infos[i], transfer, arg_infos[i]);
 
                 if (PyErr_Occurred()) {
                     /* TODO: release previous input arguments. */
@@ -1212,6 +1222,13 @@ _wrap_g_function_info_invoke (PyGIBaseInfo *self,
     }
 
 out:
+    /* Free any scope call callbacks */
+    g_slist_foreach(call_free_list, (GFunc)_pygi_callback_invoke_closure_free, NULL);
+    g_slist_free(call_free_list);
+
+    g_slist_foreach(destroy_notify_infos, (GFunc)_pygi_callback_notify_info_free, NULL);
+    g_slist_free(destroy_notify_infos);
+
     g_base_info_unref((GIBaseInfo *)return_type_info);
 
     for (i = 0; i < n_args; i++) {
@@ -1997,7 +2014,7 @@ _wrap_g_field_info_set_value (PyGIBaseInfo *self,
                     goto out;
                 }
 
-                value = _pygi_argument_from_object(py_value, field_type_info, GI_TRANSFER_NOTHING);
+                value = _pygi_argument_from_object(py_value, field_type_info, GI_TRANSFER_NOTHING, NULL);
                 if (PyErr_Occurred()) {
                     g_base_info_unref(info);
                     goto out;
@@ -2022,7 +2039,7 @@ _wrap_g_field_info_set_value (PyGIBaseInfo *self,
         g_base_info_unref(info);
     }
 
-    value = _pygi_argument_from_object(py_value, field_type_info, GI_TRANSFER_EVERYTHING);
+    value = _pygi_argument_from_object(py_value, field_type_info, GI_TRANSFER_EVERYTHING, NULL);
     if (PyErr_Occurred()) {
         goto out;
     }
