| File: | build-analysis/../src/lib/s4/src/lib/s4.c |
| Warning: | line 305, column 16 Assigned value is garbage or undefined |
| 1 | /* S4 - An XMMS2 medialib backend | |||
| 2 | * Copyright (C) 2009, 2010 Sivert Berg | |||
| 3 | * | |||
| 4 | * This library is free software; you can redistribute it and/or | |||
| 5 | * modify it under the terms of the GNU Lesser General Public | |||
| 6 | * License as published by the Free Software Foundation; either | |||
| 7 | * version 2.1 of the License, or (at your option) any later version. | |||
| 8 | * | |||
| 9 | * This library is distributed in the hope that it will be useful, | |||
| 10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |||
| 11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |||
| 12 | * Lesser General Public License for more details. | |||
| 13 | */ | |||
| 14 | ||||
| 15 | #include <s4.h> | |||
| 16 | #include "s4_priv.h" | |||
| 17 | #include "logging.h" | |||
| 18 | #include <string.h> | |||
| 19 | #include <stdlib.h> | |||
| 20 | #include <glib/gstdio.h> | |||
| 21 | #include <errno(*__errno_location ()).h> | |||
| 22 | ||||
| 23 | static GPrivate _errno = G_PRIVATE_INIT (g_free){ ((void*)0), (g_free), { ((void*)0), ((void*)0) } }; | |||
| 24 | ||||
| 25 | /** | |||
| 26 | * | |||
| 27 | * @defgroup S4 S4 | |||
| 28 | * @brief A database backend for XMMS2 | |||
| 29 | * | |||
| 30 | * @{ | |||
| 31 | */ | |||
| 32 | ||||
| 33 | #define S4_MAGIC("s4db") ("s4db") | |||
| 34 | #define S4_MAGIC_LEN(4) (4) | |||
| 35 | #define S4_VERSION1 1 | |||
| 36 | ||||
| 37 | typedef struct { | |||
| 38 | char magic[S4_MAGIC_LEN(4)]; | |||
| 39 | int32_t version; | |||
| 40 | unsigned char uuid[16]; | |||
| 41 | log_number_t last_checkpoint; | |||
| 42 | } s4_header_t; | |||
| 43 | ||||
| 44 | /** | |||
| 45 | * @{ | |||
| 46 | * @internal | |||
| 47 | */ | |||
| 48 | ||||
| 49 | /** | |||
| 50 | * Reads strings from a file | |||
| 51 | * | |||
| 52 | * @param s4 The database to add the strings to | |||
| 53 | * @param file The file to read from | |||
| 54 | * @return A hashtable with the id as they key and the | |||
| 55 | * corresponding string as they values or NULL on error | |||
| 56 | */ | |||
| 57 | static GHashTable *_read_string (s4_t *s4, FILE *file) | |||
| 58 | { | |||
| 59 | size_t r; | |||
| 60 | int32_t id, len; | |||
| 61 | char *str; | |||
| 62 | GHashTable *ret = g_hash_table_new (NULL((void*)0), NULL((void*)0)); | |||
| 63 | ||||
| 64 | while ((r = fread (&id, sizeof (int32_t), 1, file)) == 1 && | |||
| 65 | id != -1 && | |||
| 66 | (r = fread (&len, sizeof (int32_t), 1, file)) == 1) { | |||
| 67 | str = malloc (len + 1); | |||
| 68 | r = fread (str, 1, len, file); | |||
| 69 | ||||
| 70 | if (r != len) { | |||
| 71 | g_hash_table_destroy (ret); | |||
| 72 | return NULL((void*)0); | |||
| 73 | } | |||
| 74 | ||||
| 75 | str[len] = '\0'; | |||
| 76 | g_hash_table_insert (ret, GINT_TO_POINTER (id)((gpointer) (glong) (id)), (void*)_string_lookup (s4, str)); | |||
| 77 | free (str); | |||
| 78 | } | |||
| 79 | ||||
| 80 | if (r == 0) { | |||
| 81 | g_hash_table_destroy (ret); | |||
| 82 | return NULL((void*)0); | |||
| 83 | } | |||
| 84 | ||||
| 85 | return ret; | |||
| 86 | } | |||
| 87 | ||||
| 88 | /** | |||
| 89 | * Reads relations from a file | |||
| 90 | * | |||
| 91 | * @param s4 The database to insert them into | |||
| 92 | * @param file The file to read | |||
| 93 | * @param strings A hashtable with string->int relationships | |||
| 94 | * @return -1 on error, 0 otherwise | |||
| 95 | */ | |||
| 96 | static int _read_relations (s4_t *s4, FILE *file, GHashTable *strings) | |||
| 97 | { | |||
| 98 | s4_intpair_t rec; | |||
| 99 | ||||
| 100 | while (fread (&rec, sizeof (s4_intpair_t), 1, file) == 1) { | |||
| 101 | const char *key_a, *key_b, *src; | |||
| 102 | const s4_val_t *val_a, *val_b; | |||
| 103 | ||||
| 104 | key_a = g_hash_table_lookup (strings, GINT_TO_POINTER (ABS (rec.key_a))((gpointer) (glong) ((((rec.key_a) < 0) ? -(rec.key_a) : ( rec.key_a))))); | |||
| 105 | key_b = g_hash_table_lookup (strings, GINT_TO_POINTER (ABS (rec.key_b))((gpointer) (glong) ((((rec.key_b) < 0) ? -(rec.key_b) : ( rec.key_b))))); | |||
| 106 | src = g_hash_table_lookup (strings, GINT_TO_POINTER (ABS (rec.src))((gpointer) (glong) ((((rec.src) < 0) ? -(rec.src) : (rec. src))))); | |||
| 107 | ||||
| 108 | if (rec.key_a > 0) { | |||
| 109 | val_a = _string_lookup_val (s4, | |||
| 110 | g_hash_table_lookup (strings, GINT_TO_POINTER (rec.val_a)((gpointer) (glong) (rec.val_a)))); | |||
| 111 | } else { | |||
| 112 | val_a = _int_lookup_val (s4, rec.val_a); | |||
| 113 | } | |||
| 114 | if (rec.key_b > 0) { | |||
| 115 | val_b = _string_lookup_val (s4, | |||
| 116 | g_hash_table_lookup (strings, GINT_TO_POINTER (rec.val_b)((gpointer) (glong) (rec.val_b)))); | |||
| 117 | } else { | |||
| 118 | val_b = _int_lookup_val (s4, rec.val_b); | |||
| 119 | } | |||
| 120 | ||||
| 121 | _s4_add_internal (s4, key_a, val_a, key_b, val_b, src); | |||
| 122 | } | |||
| 123 | ||||
| 124 | return 0; | |||
| 125 | } | |||
| 126 | ||||
| 127 | /** | |||
| 128 | * Reads an S4 database from filename. | |||
| 129 | * | |||
| 130 | * @param s4 The s4 database to read the data into | |||
| 131 | * @param filename The name of the file to read from | |||
| 132 | * @param flags Flags passed to s4_open | |||
| 133 | * @return 0 on success, non-zero on error | |||
| 134 | */ | |||
| 135 | static int _read_file (s4_t *s4, const char *filename, int flags) | |||
| 136 | { | |||
| 137 | FILE *file = fopen (filename, "r"); | |||
| 138 | s4_header_t hdr; | |||
| 139 | int i; | |||
| 140 | ||||
| 141 | if (file == NULL((void*)0)) { | |||
| 142 | int ret = 0; | |||
| 143 | switch (errno(*__errno_location ())) { | |||
| 144 | case ENOENT2: | |||
| 145 | if (flags & S4_EXISTS) { | |||
| 146 | s4_set_errno (S4E_NOENT); | |||
| 147 | ret = -1; | |||
| 148 | } else { | |||
| 149 | s4_create_uuid (s4->uuid); | |||
| 150 | } | |||
| 151 | break; | |||
| 152 | default: | |||
| 153 | s4_set_errno (S4E_OPEN); | |||
| 154 | ret = -1; | |||
| 155 | break; | |||
| 156 | } | |||
| 157 | return ret; | |||
| 158 | } else if (flags & S4_NEW) { | |||
| 159 | fclose (file); | |||
| 160 | s4_set_errno (S4E_EXISTS); | |||
| 161 | return -1; | |||
| 162 | } | |||
| 163 | ||||
| 164 | fread (&hdr, sizeof (s4_header_t), 1, file); | |||
| 165 | if (strncmp (S4_MAGIC("s4db"), hdr.magic, S4_MAGIC_LEN(4))) { | |||
| 166 | fclose (file); | |||
| 167 | s4_set_errno (S4E_MAGIC); | |||
| 168 | return -1; | |||
| 169 | } | |||
| 170 | ||||
| 171 | if (hdr.version != S4_VERSION1) { | |||
| 172 | fclose (file); | |||
| 173 | s4_set_errno (S4E_VERSION); | |||
| 174 | return -1; | |||
| 175 | } | |||
| 176 | ||||
| 177 | _log_init (s4, hdr.last_checkpoint); | |||
| 178 | ||||
| 179 | for (i = 0; i < 16; i++) { | |||
| 180 | s4->uuid[i] = hdr.uuid[i]; | |||
| 181 | } | |||
| 182 | ||||
| 183 | GHashTable *strings = _read_string (s4, file); | |||
| 184 | if (strings == NULL((void*)0) || _read_relations (s4, file, strings) == -1) { | |||
| 185 | fclose (file); | |||
| 186 | s4_set_errno (S4E_INCONS); | |||
| 187 | return -1; | |||
| 188 | } | |||
| 189 | g_hash_table_destroy (strings); | |||
| 190 | ||||
| 191 | fclose (file); | |||
| 192 | return 0; | |||
| 193 | } | |||
| 194 | ||||
| 195 | int _reread_file (s4_t *s4) | |||
| 196 | { | |||
| 197 | _free_relations (s4); | |||
| 198 | ||||
| 199 | _index_free_data (s4->index_data); | |||
| 200 | _entry_free_data (s4->entry_data); | |||
| 201 | s4->index_data = _index_create_data (); | |||
| 202 | s4->entry_data = _entry_create_data (); | |||
| 203 | ||||
| 204 | return _read_file (s4, s4->filename, S4_EXISTS); | |||
| 205 | } | |||
| 206 | ||||
| 207 | /** | |||
| 208 | * Writes all the id->string relations in the hash table to file | |||
| 209 | * | |||
| 210 | * @param strings The hashtable holding the key-value pairs | |||
| 211 | * @param file The file to write to | |||
| 212 | */ | |||
| 213 | static void _write_strings (GHashTable *strings, FILE *file) | |||
| 214 | { | |||
| 215 | GHashTableIter iter; | |||
| 216 | void *i; | |||
| 217 | const char *str; | |||
| 218 | ||||
| 219 | g_hash_table_iter_init (&iter, strings); | |||
| 220 | while (g_hash_table_iter_next (&iter, (void**)&str, &i)) { | |||
| 221 | int32_t len = strlen (str); | |||
| 222 | int32_t str_id = GPOINTER_TO_INT (i)((gint) (glong) (i)); | |||
| 223 | fwrite (&str_id, sizeof (int32_t), 1, file); | |||
| 224 | fwrite (&len, sizeof (int32_t), 1, file); | |||
| 225 | fwrite (str, 1, len, file); | |||
| 226 | } | |||
| 227 | } | |||
| 228 | ||||
| 229 | /** | |||
| 230 | * Writes a list of int-pairs to file | |||
| 231 | * | |||
| 232 | * @param pairs A GList with pairs to write | |||
| 233 | * @param file The file to write to | |||
| 234 | */ | |||
| 235 | static void _write_pairs (GList *pairs, FILE *file) | |||
| 236 | { | |||
| 237 | for (; pairs != NULL((void*)0); pairs = g_list_next (pairs)((pairs) ? (((GList *)(pairs))->next) : ((void*)0))) { | |||
| 238 | s4_intpair_t *pair = pairs->data; | |||
| 239 | fwrite (pair, sizeof (s4_intpair_t), 1, file); | |||
| 240 | free (pair); | |||
| 241 | } | |||
| 242 | } | |||
| 243 | ||||
| 244 | typedef struct { | |||
| 245 | GHashTable *strings; | |||
| 246 | GList *pairs; | |||
| 247 | int new_id; | |||
| 248 | } save_data_t; | |||
| 249 | ||||
| 250 | /** | |||
| 251 | * Gets the id of a string, or gives it an unique id if it doesn't have one | |||
| 252 | * | |||
| 253 | * @param sd A structure holding the strings found so far and the next free id. | |||
| 254 | * @param str The string to lookup | |||
| 255 | * @return The id associated with the string | |||
| 256 | */ | |||
| 257 | static int _get_string_number (save_data_t *sd, const char *str) | |||
| 258 | { | |||
| 259 | int i = GPOINTER_TO_INT (g_hash_table_lookup (sd->strings, str))((gint) (glong) (g_hash_table_lookup (sd->strings, str))); | |||
| 260 | ||||
| 261 | if (i == 0) { | |||
| 262 | i = sd->new_id++; | |||
| 263 | g_hash_table_insert (sd->strings, (void*)str, GINT_TO_POINTER (i)((gpointer) (glong) (i))); | |||
| 264 | } | |||
| 265 | ||||
| 266 | return i; | |||
| 267 | } | |||
| 268 | ||||
| 269 | /* | |||
| 270 | * A helper function converting a resultset into a list of int-pairs. | |||
| 271 | */ | |||
| 272 | static void _result_to_pairs (s4_resultset_t *res, save_data_t *sd) | |||
| 273 | { | |||
| 274 | const s4_resultrow_t *row; | |||
| 275 | int row_no; | |||
| 276 | ||||
| 277 | for (row_no = 0; s4_resultset_get_row (res, row_no, &row); row_no++) { | |||
| 278 | int32_t va, ka, i; | |||
| 279 | const s4_val_t *val_a, *val_b; | |||
| 280 | const char *key_a, *key_b, *src, *str; | |||
| 281 | const s4_result_t *id_res, *val_res; | |||
| 282 | ||||
| 283 | s4_resultrow_get_col (row, 0, &id_res); | |||
| 284 | s4_resultrow_get_col (row, 1, &val_res); | |||
| 285 | ||||
| 286 | val_a = s4_result_get_val (id_res); | |||
| 287 | key_a = s4_result_get_key (id_res); | |||
| 288 | ||||
| 289 | ka = _get_string_number (sd, key_a); | |||
| 290 | ||||
| 291 | if (s4_val_get_int (val_a, &i)) { | |||
| 292 | va = i; | |||
| 293 | ka = -ka; | |||
| 294 | } else if (s4_val_get_str (val_a, &str)) { | |||
| 295 | va = _get_string_number (sd, str); | |||
| 296 | } | |||
| 297 | ||||
| 298 | for (; val_res != NULL((void*)0); val_res = s4_result_next (val_res)) { | |||
| 299 | s4_intpair_t *pair = malloc (sizeof (s4_intpair_t)); | |||
| 300 | ||||
| 301 | val_b = s4_result_get_val (val_res); | |||
| 302 | key_b = s4_result_get_key (val_res); | |||
| 303 | src = s4_result_get_src (val_res); | |||
| 304 | ||||
| 305 | pair->val_a = va; | |||
| ||||
| 306 | pair->key_a = ka; | |||
| 307 | pair->key_b = _get_string_number (sd, key_b); | |||
| 308 | pair->src = _get_string_number (sd, src); | |||
| 309 | ||||
| 310 | if (s4_val_get_int (val_b, &i)) { | |||
| 311 | pair->val_b = i; | |||
| 312 | pair->key_b = -pair->key_b; | |||
| 313 | } else if (s4_val_get_str (val_b, &str)) { | |||
| 314 | pair->val_b = _get_string_number (sd, str); | |||
| 315 | } | |||
| 316 | ||||
| 317 | sd->pairs = g_list_prepend (sd->pairs, pair); | |||
| 318 | } | |||
| 319 | } | |||
| 320 | } | |||
| 321 | ||||
| 322 | /** | |||
| 323 | * Writes the database to disk | |||
| 324 | * | |||
| 325 | * @param s4 The database to write | |||
| 326 | * @return non-zero on success, 0 on error | |||
| 327 | */ | |||
| 328 | static int _write_file (s4_t *s4) | |||
| 329 | { | |||
| 330 | int32_t i = -1; | |||
| 331 | int j; | |||
| 332 | FILE *file; | |||
| 333 | s4_header_t hdr; | |||
| 334 | save_data_t sd; | |||
| 335 | s4_condition_t *cond; | |||
| 336 | s4_fetchspec_t *fs; | |||
| 337 | s4_resultset_t *res; | |||
| 338 | s4_transaction_t *trans; | |||
| 339 | ||||
| 340 | _log_lock_db (s4); | |||
| 341 | ||||
| 342 | file = fopen (s4->tmp_filename, "w"); | |||
| 343 | if (file == NULL((void*)0)) { | |||
| 344 | _log_unlock_db (s4); | |||
| 345 | return 0; | |||
| 346 | } | |||
| 347 | ||||
| 348 | sd.strings = g_hash_table_new (NULL((void*)0), NULL((void*)0)); | |||
| 349 | sd.pairs = NULL((void*)0); | |||
| 350 | sd.new_id = 1; | |||
| 351 | ||||
| 352 | cond = s4_cond_new_filter (S4_FILTER_EXISTS, NULL((void*)0), NULL((void*)0), NULL((void*)0), S4_CMP_BINARY, 0); | |||
| 353 | ||||
| 354 | fs = s4_fetchspec_create (); | |||
| 355 | s4_fetchspec_add (fs, NULL((void*)0), NULL((void*)0), S4_FETCH_PARENT1); | |||
| 356 | s4_fetchspec_add (fs, NULL((void*)0), NULL((void*)0), S4_FETCH_DATA2); | |||
| 357 | ||||
| 358 | do { | |||
| 359 | trans = s4_begin (s4, 0); | |||
| 360 | res = s4_query (trans, fs, cond); | |||
| 361 | _transaction_writing (trans); | |||
| 362 | } while (!s4_commit (trans)); | |||
| 363 | ||||
| 364 | _result_to_pairs (res, &sd); | |||
| 365 | ||||
| 366 | s4_cond_free (cond); | |||
| 367 | s4_fetchspec_free (fs); | |||
| 368 | s4_resultset_free (res); | |||
| 369 | ||||
| 370 | strncpy (hdr.magic, S4_MAGIC("s4db"), S4_MAGIC_LEN(4)); | |||
| 371 | hdr.version = S4_VERSION1; | |||
| 372 | for (j = 0; j < 16; j++) { | |||
| 373 | hdr.uuid[j] = s4->uuid[j]; | |||
| 374 | } | |||
| 375 | hdr.last_checkpoint = _log_last_synced (s4); | |||
| 376 | ||||
| 377 | fwrite (&hdr, sizeof (s4_header_t), 1, file); | |||
| 378 | _write_strings (sd.strings, file); | |||
| 379 | fwrite (&i, sizeof (int32_t), 1, file); | |||
| 380 | _write_pairs (sd.pairs, file); | |||
| 381 | ||||
| 382 | g_hash_table_destroy (sd.strings); | |||
| 383 | g_list_free (sd.pairs); | |||
| 384 | ||||
| 385 | fclose (file); | |||
| 386 | ||||
| 387 | g_renamerename (s4->tmp_filename, s4->filename); | |||
| 388 | ||||
| 389 | _log_checkpoint (s4); | |||
| 390 | _log_unlock_db (s4); | |||
| 391 | return 1; | |||
| 392 | } | |||
| 393 | ||||
| 394 | static void *_sync_thread (s4_t *s4) | |||
| 395 | { | |||
| 396 | g_mutex_lock (&s4->sync_lock); | |||
| 397 | while (s4->sync_thread_run) { | |||
| ||||
| 398 | if (s4->sync_thread_run) | |||
| 399 | g_cond_wait (&s4->sync_cond, &s4->sync_lock); | |||
| 400 | g_mutex_unlock (&s4->sync_lock); | |||
| 401 | ||||
| 402 | s4_sync (s4); | |||
| 403 | ||||
| 404 | g_mutex_lock (&s4->sync_lock); | |||
| 405 | g_cond_broadcast (&s4->sync_finished_cond); | |||
| 406 | } | |||
| 407 | g_mutex_unlock (&s4->sync_lock); | |||
| 408 | ||||
| 409 | return NULL((void*)0); | |||
| 410 | } | |||
| 411 | ||||
| 412 | /* Start sync */ | |||
| 413 | void _start_sync (s4_t *s4) | |||
| 414 | { | |||
| 415 | g_mutex_lock (&s4->sync_lock); | |||
| 416 | g_cond_signal (&s4->sync_cond); | |||
| 417 | g_mutex_unlock (&s4->sync_lock); | |||
| 418 | } | |||
| 419 | ||||
| 420 | /* Start and wait for sync to finish */ | |||
| 421 | void _sync (s4_t *s4) | |||
| 422 | { | |||
| 423 | g_mutex_lock (&s4->sync_lock); | |||
| 424 | g_cond_signal (&s4->sync_cond); | |||
| 425 | g_cond_wait (&s4->sync_finished_cond, &s4->sync_lock); | |||
| 426 | g_mutex_unlock (&s4->sync_lock); | |||
| 427 | } | |||
| 428 | ||||
| 429 | static s4_t *_alloc (void) | |||
| 430 | { | |||
| 431 | s4_t* s4 = calloc (1, sizeof(s4_t)); | |||
| 432 | ||||
| 433 | g_mutex_init (&s4->sync_lock); | |||
| 434 | g_cond_init (&s4->sync_cond); | |||
| 435 | g_cond_init (&s4->sync_finished_cond); | |||
| 436 | ||||
| 437 | s4->const_data = _const_create_data (); | |||
| 438 | s4->index_data = _index_create_data (); | |||
| 439 | s4->entry_data = _entry_create_data (); | |||
| 440 | s4->log_data = _log_create_data (); | |||
| 441 | ||||
| 442 | return s4; | |||
| 443 | } | |||
| 444 | ||||
| 445 | /** | |||
| 446 | * Frees an S4 handle and everything it points at | |||
| 447 | * | |||
| 448 | * @param s4 The handle to free | |||
| 449 | */ | |||
| 450 | static void _free (s4_t *s4) | |||
| 451 | { | |||
| 452 | _free_relations (s4); | |||
| 453 | ||||
| 454 | g_mutex_clear (&s4->sync_lock); | |||
| 455 | g_cond_clear (&s4->sync_cond); | |||
| 456 | g_cond_clear (&s4->sync_finished_cond); | |||
| 457 | ||||
| 458 | _const_free_data (s4->const_data); | |||
| 459 | _index_free_data (s4->index_data); | |||
| 460 | _entry_free_data (s4->entry_data); | |||
| 461 | _log_free_data (s4->log_data); | |||
| 462 | ||||
| 463 | free (s4->filename); | |||
| 464 | g_free (s4->tmp_filename); | |||
| 465 | free (s4); | |||
| 466 | } | |||
| 467 | ||||
| 468 | /** | |||
| 469 | * @} | |||
| 470 | */ | |||
| 471 | ||||
| 472 | /** | |||
| 473 | * Opens an S4 database | |||
| 474 | * | |||
| 475 | * @b The different flags you can pass: | |||
| 476 | * <P> | |||
| 477 | * @b S4_NEW | |||
| 478 | * <BR> | |||
| 479 | * It will create a new file if one does not already exists. | |||
| 480 | * If one exists it will fail and return NULL. | |||
| 481 | * </P><P> | |||
| 482 | * @b S4_EXISTS | |||
| 483 | * <BR> | |||
| 484 | * If the file does not exists it will fail and return NULL. | |||
| 485 | * s4_errno may be used to get more information about what | |||
| 486 | * went wrong. | |||
| 487 | * </P><P> | |||
| 488 | * @b S4_MEMORY | |||
| 489 | * Creates a memory-only database. It will not read any files | |||
| 490 | * on startup or write files on shutdown. Use this if you want | |||
| 491 | * a temporary database. | |||
| 492 | * <BR> | |||
| 493 | * | |||
| 494 | * @param filename The name of the file containing the database | |||
| 495 | * @param indices An array of keys to have indices on | |||
| 496 | * @param open_flags Zero or more of the flags bitwise-or'd. | |||
| 497 | * @return A pointer to an s4_t, or NULL if something went wrong. | |||
| 498 | */ | |||
| 499 | s4_t *s4_open (const char *filename, const char **indices, int open_flags) | |||
| 500 | { | |||
| 501 | int i; | |||
| 502 | s4_t *s4; | |||
| 503 | ||||
| 504 | s4 = _alloc (); | |||
| 505 | ||||
| 506 | for (i = 0; indices != NULL((void*)0) && indices[i] != NULL((void*)0); i++) { | |||
| 507 | _index_add (s4, indices[i], _index_create ()); | |||
| 508 | } | |||
| 509 | ||||
| 510 | s4->open_flags = open_flags; | |||
| 511 | ||||
| 512 | if (open_flags & S4_MEMORY) { | |||
| 513 | return s4; | |||
| 514 | } | |||
| 515 | ||||
| 516 | s4->filename = strdup (filename); | |||
| 517 | s4->tmp_filename = g_strconcat (filename, ".chkpnt", NULL((void*)0)); | |||
| 518 | if (_read_file (s4, s4->filename, open_flags)) { | |||
| 519 | _free (s4); | |||
| 520 | return NULL((void*)0); | |||
| 521 | } | |||
| 522 | ||||
| 523 | if (!_log_open (s4)) { | |||
| 524 | _free (s4); | |||
| 525 | return NULL((void*)0); | |||
| 526 | } | |||
| 527 | ||||
| 528 | /* Write the file right away in case we have opened a new file | |||
| 529 | * or we redid something in the log that was not in the file | |||
| 530 | */ | |||
| 531 | s4_sync (s4); | |||
| 532 | ||||
| 533 | s4->sync_thread_run = 1; | |||
| 534 | s4->sync_thread = g_thread_new ("s4 sync", (GThreadFunc)_sync_thread, s4); | |||
| 535 | ||||
| 536 | return s4; | |||
| 537 | } | |||
| 538 | ||||
| 539 | /** | |||
| 540 | * Closes an open S4 database | |||
| 541 | * | |||
| 542 | * @param s4 The database to close | |||
| 543 | * | |||
| 544 | */ | |||
| 545 | int s4_close (s4_t* s4) | |||
| 546 | { | |||
| 547 | if (!(s4->open_flags & S4_MEMORY)) { | |||
| 548 | g_mutex_lock (&s4->sync_lock); | |||
| 549 | s4->sync_thread_run = 0; | |||
| 550 | g_cond_signal (&s4->sync_cond); | |||
| 551 | g_mutex_unlock (&s4->sync_lock); | |||
| 552 | g_thread_join (s4->sync_thread); | |||
| 553 | ||||
| 554 | _log_close (s4); | |||
| 555 | } | |||
| 556 | ||||
| 557 | _free (s4); | |||
| 558 | ||||
| 559 | return 0; | |||
| 560 | } | |||
| 561 | ||||
| 562 | ||||
| 563 | /** | |||
| 564 | * Writes all changes to disk | |||
| 565 | * | |||
| 566 | * @param s4 The database to sync | |||
| 567 | * | |||
| 568 | */ | |||
| 569 | void s4_sync (s4_t *s4) | |||
| 570 | { | |||
| 571 | if (!_write_file (s4)) { | |||
| 572 | S4_ERROR ("s4_sync: could not write file")g_warning ("../src/lib/s4/src/lib/s4.c" ":" "572" ": " "s4_sync: could not write file" ); | |||
| 573 | } | |||
| 574 | } | |||
| 575 | ||||
| 576 | /** | |||
| 577 | * Returns the last error number set. | |||
| 578 | * This function is thread safe, error numbers set in one thread | |||
| 579 | * will NOT be seen in another thread. | |||
| 580 | * | |||
| 581 | * @return The last error number set, or S4E_NOERROR if none has been set | |||
| 582 | */ | |||
| 583 | s4_errno_t s4_errno() | |||
| 584 | { | |||
| 585 | s4_errno_t *i = g_private_get (&_errno); | |||
| 586 | if (i == NULL((void*)0)) { | |||
| 587 | return S4E_NOERROR; | |||
| 588 | } | |||
| 589 | return *i; | |||
| 590 | } | |||
| 591 | ||||
| 592 | /** | |||
| 593 | * Sets errno to the given error number | |||
| 594 | * | |||
| 595 | * @param err The error number to set | |||
| 596 | */ | |||
| 597 | void s4_set_errno (s4_errno_t err) | |||
| 598 | { | |||
| 599 | s4_errno_t *i = g_private_get (&_errno); | |||
| 600 | if (i == NULL((void*)0)) { | |||
| 601 | i = malloc (sizeof (s4_errno_t)); | |||
| 602 | g_private_set (&_errno, i); | |||
| 603 | } | |||
| 604 | ||||
| 605 | *i = err; | |||
| 606 | } | |||
| 607 | ||||
| 608 | /** | |||
| 609 | * @} | |||
| 610 | */ |