Make links between cloud log entries direct.
[bluesky.git] / bluesky / cloudlog.c
index c99d181..30c2db0 100644 (file)
@@ -91,12 +91,22 @@ BlueSkyCloudLog *bluesky_cloudlog_new(BlueSkyFS *fs)
     log->fs = fs;
     log->type = LOGTYPE_UNKNOWN;
     log->id = bluesky_cloudlog_new_id();
-    log->pointers = g_array_new(FALSE, TRUE, sizeof(BlueSkyCloudID));
+    log->links = g_array_new(FALSE, TRUE, sizeof(BlueSkyCloudLog *));
     g_atomic_int_set(&log->refcount, 1);
 
     return log;
 }
 
+/* The reference held by the hash table does not count towards the reference
+ * count.  When a new object is created, it initially has a reference count of
+ * 1 for the creator, and similarly fetching an item from the hash table will
+ * also create a reference.  If the reference count drops to zero,
+ * bluesky_cloudlog_unref attempts to remove the object from the hash
+ * table--but there is a potential race since another thread might read the
+ * object from the hash table at the same time.  So an object with a reference
+ * count of zero may still be resurrected, in which case we need to abort the
+ * destruction.  Once the object is gone from the hash table, and if the
+ * reference count is still zero, it can actually be deleted. */
 void bluesky_cloudlog_ref(BlueSkyCloudLog *log)
 {
     if (log == NULL)
@@ -111,7 +121,28 @@ void bluesky_cloudlog_unref(BlueSkyCloudLog *log)
         return;
 
     if (g_atomic_int_dec_and_test(&log->refcount)) {
-        g_print("Cloud log refcount dropped to zero.\n");
+        BlueSkyFS *fs = log->fs;
+
+        g_mutex_lock(fs->lock);
+        if (g_atomic_int_get(&log->refcount) > 0) {
+            g_mutex_unlock(fs->lock);
+            return;
+        }
+
+        g_hash_table_remove(fs->locations, &log->id);
+        g_mutex_unlock(fs->lock);
+
+        log->type = LOGTYPE_INVALID;
+        g_mutex_free(log->lock);
+        g_cond_free(log->cond);
+        for (int i = 0; i < log->links->len; i++) {
+            BlueSkyCloudLog *c = g_array_index(log->links,
+                                               BlueSkyCloudLog *, i);
+            bluesky_cloudlog_unref(c);
+        }
+        g_array_unref(log->links);
+        bluesky_string_unref(log->data);
+        g_free(log);
     }
 }
 
@@ -157,19 +188,15 @@ BlueSkyCloudPointer bluesky_cloudlog_serialize(BlueSkyCloudLog *log,
     g_print("Flushing object %s to cloud...\n",
             bluesky_cloudlog_id_to_string(log->id));
 
-    for (int i = 0; i < log->pointers->len; i++) {
-        BlueSkyCloudID id = g_array_index(log->pointers, BlueSkyCloudID, i);
-        g_print("  ...checking reference %s...\n",
-                bluesky_cloudlog_id_to_string(id));
-        g_mutex_lock(log->fs->lock);
-        BlueSkyCloudLog *log2
-            = (BlueSkyCloudLog *)g_hash_table_lookup(log->fs->locations, &id);
-        // TODO: refcount
-        g_mutex_unlock(log->fs->lock);
-        g_assert(log2 != NULL);
-        bluesky_cloudlog_serialize(log2, state);
+    for (int i = 0; i < log->links->len; i++) {
+        BlueSkyCloudLog *ref = g_array_index(log->links,
+                                             BlueSkyCloudLog *, i);
+        if (ref != NULL)
+            bluesky_cloudlog_serialize(ref, state);
     }
 
+    g_mutex_lock(log->lock);
+    bluesky_cloudlog_fetch(log);
     g_assert(log->data != NULL);
 
     log->location = state->location;
@@ -188,6 +215,7 @@ BlueSkyCloudPointer bluesky_cloudlog_serialize(BlueSkyCloudLog *log,
     g_string_append_len(state->data, log->data->data, log->data->len);
 
     log->location_flags |= CLOUDLOG_CLOUD;
+    g_mutex_unlock(log->lock);
 
     return log->location;
 }
@@ -217,6 +245,7 @@ void bluesky_cloudlog_write_log(BlueSkyFS *fs)
     while (state->inode_list != NULL) {
         BlueSkyCloudLog *log = (BlueSkyCloudLog *)state->inode_list->data;
         bluesky_cloudlog_serialize(log, state);
+        bluesky_cloudlog_unref(log);
         state->inode_list = g_list_delete_link(state->inode_list,
                                                state->inode_list);
     }