Commits

Fuzhou Chen committed 93c1c51

[New] Set vector/gc enlarging configurable. Disable by default.

Comments (0)

Files changed (12)

 extern const char* ESCH_CONFIG_KEY_LOG;
 extern const char* ESCH_CONFIG_KEY_GC;
 extern const char* ESCH_CONFIG_KEY_VECTOR_ELEMENT_TYPE;
-extern const char* ESCH_CONFIG_KEY_VECTOR_INITIAL_LENGTH;
-extern const char* ESCH_CONFIG_KEY_GC_NAIVE_INITIAL_SLOTS;
+extern const char* ESCH_CONFIG_KEY_VECTOR_LENGTH;
+extern const char* ESCH_CONFIG_KEY_VECTOR_ENLARGE;
+extern const char* ESCH_CONFIG_KEY_GC_NAIVE_SLOTS;
 extern const char* ESCH_CONFIG_KEY_GC_NAIVE_ROOT;
+extern const char* ESCH_CONFIG_KEY_GC_NAIVE_ENLARGE;
 
 typedef enum esch_error {
     ESCH_OK = 0,
     ESCH_ERROR_OBJECT_UNEXPECTED_GC_ATTACHED,
     ESCH_ERROR_GC_ROOT_MISSING,
     ESCH_ERROR_GC_ROOT_NOT_CONTAINER,
+    ESCH_ERROR_CONTAINER_FULL,
 } esch_error;
 
 typedef enum esch_config_value_type {

src/esch_config.c

 const char* ESCH_CONFIG_KEY_ALLOC = "common:alloc";
 const char* ESCH_CONFIG_KEY_LOG = "common:log";
 const char* ESCH_CONFIG_KEY_GC = "common:gc";
-const char* ESCH_CONFIG_KEY_VECTOR_INITIAL_LENGTH = "vector:initial_length";
-const char* ESCH_CONFIG_KEY_GC_NAIVE_INITIAL_SLOTS = "gc:naive:initial_slots";
+const char* ESCH_CONFIG_KEY_VECTOR_LENGTH = "vector:length";
+const char* ESCH_CONFIG_KEY_VECTOR_ENLARGE = "vector:enlarge";
+const char* ESCH_CONFIG_KEY_GC_NAIVE_SLOTS = "gc:naive:slots";
 const char* ESCH_CONFIG_KEY_GC_NAIVE_ROOT = "gc:naive:root";
+const char* ESCH_CONFIG_KEY_GC_NAIVE_ENLARGE = "gc:naive:enlarge";
 
 static esch_error esch_config_destructor(esch_object* obj);
 static esch_error esch_config_new_as_object(esch_config*, esch_object** obj);
     strncpy(new_config->config[0].key,
             ESCH_CONFIG_KEY_ALLOC, ESCH_CONFIG_KEY_LENGTH);
     new_config->config[0].type = ESCH_CONFIG_VALUE_TYPE_OBJECT;
+
     strncpy(new_config->config[1].key,
             ESCH_CONFIG_KEY_LOG, ESCH_CONFIG_KEY_LENGTH);
     new_config->config[1].type = ESCH_CONFIG_VALUE_TYPE_OBJECT;
+
     strncpy(new_config->config[2].key,
             ESCH_CONFIG_KEY_GC, ESCH_CONFIG_KEY_LENGTH);
     new_config->config[2].type = ESCH_CONFIG_VALUE_TYPE_OBJECT;
 
     strncpy(new_config->config[3].key,
-            ESCH_CONFIG_KEY_VECTOR_INITIAL_LENGTH, ESCH_CONFIG_KEY_LENGTH);
+            ESCH_CONFIG_KEY_VECTOR_LENGTH, ESCH_CONFIG_KEY_LENGTH);
     new_config->config[3].type = ESCH_CONFIG_VALUE_TYPE_INTEGER;
     new_config->config[3].data.int_value = 1;
 
     strncpy(new_config->config[4].key,
-            ESCH_CONFIG_KEY_GC_NAIVE_INITIAL_SLOTS, ESCH_CONFIG_KEY_LENGTH);
+            ESCH_CONFIG_KEY_VECTOR_ENLARGE, ESCH_CONFIG_KEY_LENGTH);
     new_config->config[4].type = ESCH_CONFIG_VALUE_TYPE_INTEGER;
-    new_config->config[4].data.int_value = ESCH_GC_NAIVE_DEFAULT_SLOTS;
+    new_config->config[4].data.int_value = ESCH_FALSE;
 
     strncpy(new_config->config[5].key,
+            ESCH_CONFIG_KEY_GC_NAIVE_SLOTS, ESCH_CONFIG_KEY_LENGTH);
+    new_config->config[5].type = ESCH_CONFIG_VALUE_TYPE_INTEGER;
+    new_config->config[5].data.int_value = ESCH_GC_NAIVE_DEFAULT_SLOTS;
+
+    strncpy(new_config->config[6].key,
             ESCH_CONFIG_KEY_GC_NAIVE_ROOT, ESCH_CONFIG_KEY_LENGTH);
-    new_config->config[5].type = ESCH_CONFIG_VALUE_TYPE_OBJECT;
-    new_config->config[5].data.obj_value = NULL;
+    new_config->config[6].type = ESCH_CONFIG_VALUE_TYPE_OBJECT;
+    new_config->config[6].data.obj_value = NULL;
 
+    strncpy(new_config->config[7].key,
+            ESCH_CONFIG_KEY_GC_NAIVE_ENLARGE, ESCH_CONFIG_KEY_LENGTH);
+    new_config->config[7].type = ESCH_CONFIG_VALUE_TYPE_INTEGER;
+    new_config->config[7].data.int_value = ESCH_FALSE;
 
     (*config) = new_config;
     new_config = NULL;

src/esch_config.h

 
 #define ESCH_CONFIG_KEY_LENGTH 32
 #define ESCH_CONFIG_VALUE_STRING_LENGTH 255
-#define ESCH_CONFIG_ITEMS 6
+#define ESCH_CONFIG_ITEMS 8
 struct esch_config
 {
     /*
     ((esch_object*)(cfg->config[1].data.obj_value))
 #define ESCH_CONFIG_GET_GC(cfg) \
     ((esch_object*)(cfg->config[2].data.obj_value))
-#define ESCH_CONFIG_GET_VECOTR_INITIAL_LENGTH(cfg) \
+#define ESCH_CONFIG_GET_VECOTR_LENGTH(cfg) \
     ((int)(cfg->config[3].data.int_value))
-#define ESCH_CONFIG_GET_GC_NAIVE_INITIAL_SLOTS(cfg) \
+#define ESCH_CONFIG_GET_VECTOR_ENLARGE(cfg) \
     ((int)(cfg->config[4].data.int_value))
+#define ESCH_CONFIG_GET_GC_NAIVE_SLOTS(cfg) \
+    ((int)(cfg->config[5].data.int_value))
 #define ESCH_CONFIG_GET_GC_NAIVE_ROOT(cfg) \
-    ((esch_object*)(cfg->config[5].data.obj_value))
+    ((esch_object*)(cfg->config[6].data.obj_value))
+#define ESCH_CONFIG_GET_GC_NAIVE_ENLARGE(cfg) \
+    ((int)(cfg->config[7].data.int_value))
 
 #ifdef __cplusplus
 }
             ESCH_ERROR_INVALID_STATE);
 
     if (gc->slots[gc->usable_slot].obj == gc->root) {
-        /* Running out of slots. Need to allocate new */
-        int i = 0;
-        (void)esch_log_info(log,
-                "gc:attach: No more slot. Reallocate. %d",
-                gc->slot_count);
-        new_count = gc->slot_count * 2;
+        if (gc->enlarge) {
+            /* Running out of slots. */
+            int i = 0;
+            (void)esch_log_info(log,
+                    "gc:attach: No more slot. Reallocate. %d",
+                    gc->slot_count);
+            new_count = gc->slot_count * 2;
 
-        /* Reallocate a larger buffer */
-        ret = esch_alloc_realloc_i(alloc, gc->slots,
-                         sizeof(union esch_object_or_next) * new_count,
-                         (void**)&new_slots);
-        ESCH_CHECK(ret == ESCH_OK, log,
-                "gc:attach: FATAL: Can't allocate new slots", ret);
-        ret = esch_alloc_realloc_i(alloc, gc->inuse_flags,
-                                   sizeof(esch_byte) / 8 * new_count,
-                                   (void**)&new_flags);
-        ESCH_CHECK(ret == ESCH_OK, log,
-                "gc:attach: FATAL: Can't allocate new flags", ret);
-        ret = esch_alloc_realloc_i(alloc, gc->recycle_stack,
-                                   sizeof(esch_object*) * (new_count + 1),
-                                   (void**)&new_recycle_stack);
-        ESCH_CHECK(ret == ESCH_OK, log,
-                   "GC:attach:Can't allocate new recycle stack", ret);
+            /* Reallocate a larger buffer */
+            ret = esch_alloc_realloc_i(alloc, gc->slots,
+                             sizeof(union esch_object_or_next) * new_count,
+                             (void**)&new_slots);
+            ESCH_CHECK(ret == ESCH_OK, log,
+                    "gc:attach: FATAL: Can't allocate new slots", ret);
+            ret = esch_alloc_realloc_i(alloc, gc->inuse_flags,
+                                       sizeof(esch_byte) / 8 * new_count,
+                                       (void**)&new_flags);
+            ESCH_CHECK(ret == ESCH_OK, log,
+                    "gc:attach: FATAL: Can't allocate new flags", ret);
+            ret = esch_alloc_realloc_i(alloc, gc->recycle_stack,
+                                sizeof(esch_object*) * (new_count + 1),
+                                (void**)&new_recycle_stack);
+            ESCH_CHECK(ret == ESCH_OK, log,
+                       "GC:attach:Can't allocate new recycle stack", ret);
 
-        /* Content of original buffer has been moved to new buffer,
-         * Now update availability slot list. */
-        new_slots[gc->slot_count].obj = gc->root;
-        for (i = gc->slot_count + 1; i < new_count - 1; ++i) {
-            new_slots[i + 1].next = i;
+            /* Content of original buffer has been moved to new buffer,
+             * Now update availability slot list. */
+            new_slots[gc->slot_count].obj = gc->root;
+            for (i = gc->slot_count + 1; i < new_count - 1; ++i) {
+                new_slots[i + 1].next = i;
+            }
+
+            gc->slot_count = new_count;
+            gc->slots = new_slots;
+            gc->inuse_flags = new_flags;
+            gc->recycle_stack = new_recycle_stack;
+            /* Always count availability chain from last element */
+            gc->usable_slot = new_count - 1;
+
+            new_slots = NULL;
+            new_flags = NULL;
+        } else {
+            esch_log_error(log, "gc:attach:Enlarge is disabled.");
+            ret = ESCH_ERROR_CONTAINER_FULL;
+            goto Exit;
         }
-
-        gc->slot_count = new_count;
-        gc->slots = new_slots;
-        gc->inuse_flags = new_flags;
-        gc->recycle_stack = new_recycle_stack;
-        /* Always count availability chain from last element */
-        gc->usable_slot = new_count -1;
-
-        new_slots = NULL;
-        new_flags = NULL;
     }
     new_offset = gc->usable_slot;
     (void)esch_log_info(log,
-            "gc:attach: Allocate new slot: id: %d", new_offset);
+            "gc:attach:Allocate new slot: id: %d", new_offset);
 
     gc->usable_slot = gc->slots[gc->usable_slot].next;
     allocated_slot = &(gc->slots[new_offset].obj);
     esch_object** recycle_stack = NULL;
     int i = 0;
 
-    initial_slots = ESCH_CONFIG_GET_GC_NAIVE_INITIAL_SLOTS(config);
+    initial_slots = ESCH_CONFIG_GET_GC_NAIVE_SLOTS(config);
     if (initial_slots <= ESCH_GC_NAIVE_DEFAULT_SLOTS) {
         initial_slots = ESCH_GC_NAIVE_DEFAULT_SLOTS;
     }
     }
 
     /* Always start from last slot */
-    new_gc->usable_slot = (initial_slots - 1);
+    new_gc->enlarge = (ESCH_CONFIG_GET_GC_NAIVE_ENLARGE(config)?
+                       ESCH_TRUE: ESCH_FALSE);
+    new_gc->usable_slot = (initial_slots - 1); /* Allocate from last */
     new_gc->attach = esch_gc_naive_mark_sweep_attach_i;
     new_gc->recycle = esch_gc_naive_mark_sweep_recycle_i;
     new_gc->inuse_flags = inuse_flags;
     esch_gc_attach_f     attach;
     esch_gc_recycle_f    recycle;
 
+    esch_bool enlarge;
     unsigned char* inuse_flags;
     union esch_object_or_next* slots;
     esch_object** recycle_stack;

src/esch_vector.c

     ESCH_CHECK_PARAM_INTERNAL(ESCH_IS_VALID_ALLOC(alloc));
     ESCH_CHECK_PARAM_INTERNAL(ESCH_IS_VALID_LOG(log));
 
-    initial_length = ESCH_CONFIG_GET_VECOTR_INITIAL_LENGTH(config);
+    initial_length = ESCH_CONFIG_GET_VECOTR_LENGTH(config);
     if (initial_length < ESCH_VECTOR_MINIMAL_INITIAL_LENGTH) {
         initial_length = ESCH_VECTOR_MINIMAL_INITIAL_LENGTH;
     }
     new_vec = ESCH_CAST_FROM_OBJECT(vec_obj, esch_vector);
     ESCH_ASSERT(ESCH_IS_VALID_TYPE(&(esch_vector_type.type)));
 
+    new_vec->enlarge = (ESCH_CONFIG_GET_VECTOR_ENLARGE(config)?
+                        ESCH_TRUE: ESCH_FALSE);
     new_vec->slots = (size_t)initial_length;
     new_vec->begin = array;
     new_vec->next = &(new_vec->begin[0]);
     alloc = ESCH_OBJECT_GET_ALLOC(ESCH_CAST_TO_OBJECT(vec));
     log = ESCH_OBJECT_GET_LOG(ESCH_CAST_TO_OBJECT(vec));
 
-    if (vec->next == vec->begin + vec->slots) /* vector buffer is full */
-    {
-        new_slots = vec->slots * 2;
+    if (vec->next == vec->begin + vec->slots) {
+        /* vector buffer is full */
+        if (vec->enlarge) {
+            new_slots = vec->slots * 2;
+            ESCH_ASSERT(alloc != NULL);
+            ESCH_ASSERT(ESCH_IS_VALID_ALLOC(alloc));
 
-        ESCH_CHECK_PARAM_INTERNAL(alloc != NULL);
-        ESCH_CHECK_PARAM_INTERNAL(ESCH_IS_VALID_ALLOC(alloc));
+            ret = esch_alloc_realloc(alloc, vec->begin,
+                                     sizeof(esch_object*) * (new_slots + 1),
+                                     (void**)&new_array);
+            ESCH_CHECK(ret == ESCH_OK, log,
+                       "vec:append:Failed to reallocate vec", ret);
 
-        ret = esch_alloc_realloc(alloc, vec->begin,
-                                sizeof(esch_object*) * (new_slots + 1),
-                                (void**)&new_array);
-        ESCH_CHECK(ret == ESCH_OK, vec, "Failed to reallocate vec", ret);
-
-        vec->begin = new_array;
-        vec->next = vec->begin + vec->slots;
-        vec->slots = new_slots;
+            vec->begin = new_array;
+            vec->next = vec->begin + vec->slots;
+            vec->slots = new_slots;
+        } else {
+            /* Enlarge is not allowed. It's by default. */
+            esch_log_error(log, "vec:append:Enlarge is disabled.");
+            ret = ESCH_ERROR_CONTAINER_FULL;
+            goto Exit;
+        }
     }
     slot = vec->next;
-    ++vec->next;
+    vec->next += 1;
     (*slot) = data;
 Exit:
     return ret;
                                   ESCH_CAST_TO_OBJECT(gc));
         ESCH_CHECK(ret == ESCH_OK, log, "vec:Can't insert gc", ret);
     }
-    ret = esch_config_set_int(config,
-                              ESCH_CONFIG_KEY_VECTOR_INITIAL_LENGTH,
+    ret = esch_config_set_int(config, ESCH_CONFIG_KEY_VECTOR_LENGTH,
                               vec->slots);
     ESCH_CHECK(ret == ESCH_OK, log, "vec:Can't set initial length", ret);
 

src/esch_vector.h

 
 struct esch_vector
 {
+    esch_bool enlarge;
     size_t slots;
     esch_object** begin;
     esch_object** next; /* Next available slot */

src/utest/esch_t_gc.c

     ESCH_TEST_CHECK(ret == ESCH_OK, "Failed to set GC root", ret);
 
     ret = esch_config_set_int(config,
-                              ESCH_CONFIG_KEY_GC_NAIVE_INITIAL_SLOTS, -1);
+                              ESCH_CONFIG_KEY_GC_NAIVE_SLOTS, -1);
     ret = esch_gc_new_naive_mark_sweep(config, &gc2);
     ESCH_TEST_CHECK(ret == ESCH_OK && gc2 != NULL,
                     "Failed to create gc2", ret);
     ESCH_TEST_CHECK(ret == ESCH_OK, "Failed to set GC root", ret);
 
     ret = esch_config_set_int(config,
-                              ESCH_CONFIG_KEY_GC_NAIVE_INITIAL_SLOTS, 256);
+                              ESCH_CONFIG_KEY_GC_NAIVE_SLOTS, 256);
     ret = esch_gc_new_naive_mark_sweep(config, &gc3);
     ESCH_TEST_CHECK(ret == ESCH_OK && gc3 != NULL,
                     "Failed to create gc3", ret);

src/utest/esch_t_vector.c

     log = ESCH_CAST_FROM_OBJECT(log_obj, esch_log);
     alloc = ESCH_CAST_FROM_OBJECT(alloc_obj, esch_alloc);
 
+    ret = esch_config_set_int(config, ESCH_CONFIG_KEY_VECTOR_ENLARGE, 1);
+
     ret = esch_vector_new(config, &vec);
     ESCH_TEST_CHECK(ret == ESCH_OK, "Failed to create vector", ret);
     ESCH_TEST_CHECK(vec->slots == ESCH_VECTOR_MINIMAL_INITIAL_LENGTH,
     ret = esch_object_delete(ESCH_CAST_TO_OBJECT(str));
     ESCH_TEST_CHECK(ret == ESCH_OK, "Failed to delete str object.", ret);
 Exit:
+    /* Set it back so other objects can initialize with default value. */
+    (void)esch_config_set_int(config, ESCH_CONFIG_KEY_VECTOR_ENLARGE, 0);
     return ret;
 }
 
     size_t length = 0;
     size_t i = 0;
 
+    ret = esch_config_set_int(config, ESCH_CONFIG_KEY_VECTOR_ENLARGE, 1);
     ret = esch_config_get_obj(config, ESCH_CONFIG_KEY_LOG, &log_obj);
     ret = esch_config_get_obj(config, ESCH_CONFIG_KEY_ALLOC, &alloc_obj);
     log = ESCH_CAST_FROM_OBJECT(log_obj, esch_log);
         ESCH_TEST_CHECK(ret == ESCH_OK, "Failed to delete str.", ret);
     }
 Exit:
+    (void)esch_config_set_int(config, ESCH_CONFIG_KEY_VECTOR_ENLARGE, 0);
     return ret;
 }
 
+esch_error test_vectorResizeFlag(esch_config* config)
+{
+    esch_error ret = ESCH_OK;
+    esch_vector* vec_fixed = NULL;
+    esch_vector* vec_dyn = NULL;
+    size_t elements = ESCH_VECTOR_MINIMAL_INITIAL_LENGTH * 3;
+    size_t i = 0;
+    size_t length = 0;
+
+    ret = esch_config_set_int(config, ESCH_CONFIG_KEY_VECTOR_LENGTH,
+                              elements);
+    ret = esch_vector_new(config, &vec_fixed);
+    ESCH_TEST_CHECK(ret == ESCH_OK, "fixed:Failed to create vector", ret);
+    ESCH_TEST_CHECK(vec_fixed->slots == elements, "fixed:Not new size", ret);
+
+    ret = esch_config_set_int(config, ESCH_CONFIG_KEY_VECTOR_ENLARGE, 1);
+    ret = esch_vector_new(config, &vec_dyn);
+    ESCH_TEST_CHECK(ret == ESCH_OK, "dyn:Failed to create vector", ret);
+    ESCH_TEST_CHECK(vec_dyn->slots == elements, "dyn:Not new size", ret);
+
+    for (i = 0; i < elements; ++i) {
+        ret = esch_vector_append(vec_fixed, ESCH_CAST_TO_OBJECT(vec_fixed));
+        ESCH_TEST_CHECK(ret == ESCH_OK, "fixed:Can't append element", ret);
+        ret = esch_vector_get_length(vec_fixed, &length);
+        ESCH_TEST_CHECK(length == i + 1, "fixed:Can't append element",
+                        ESCH_ERROR_OUT_OF_BOUND);
+    }
+
+    ret = esch_vector_append(vec_fixed, ESCH_CAST_TO_OBJECT(vec_fixed));
+    ESCH_TEST_CHECK(ret == ESCH_ERROR_CONTAINER_FULL,
+                    "fixed:(error as expected) Can't append element", ret);
+    ret = esch_vector_get_length(vec_fixed, &length);
+    ESCH_TEST_CHECK(length == elements, "fixed:Can't append element",
+                    ESCH_ERROR_INVALID_STATE);
+
+    for (i = 0; i < elements; ++i) {
+        ret = esch_vector_append(vec_dyn, ESCH_CAST_TO_OBJECT(vec_dyn));
+        ESCH_TEST_CHECK(ret == ESCH_OK, "dyn:Can't append element", ret);
+        ret = esch_vector_get_length(vec_dyn, &length);
+        ESCH_TEST_CHECK(length == i + 1, "dyn:Can't append element",
+                        ESCH_ERROR_OUT_OF_BOUND);
+    }
+    ret = esch_vector_append(vec_dyn, ESCH_CAST_TO_OBJECT(vec_dyn));
+    ESCH_TEST_CHECK(ret == ESCH_OK,
+                    "dyn:Supposed length should be enlarged", ret);
+    ret = esch_vector_get_length(vec_dyn, &length);
+    ESCH_TEST_CHECK(length == elements + 1, "dyn:Suppose should append",
+                    ESCH_ERROR_INVALID_STATE);
+
+Exit:
+    if (vec_fixed != NULL) {
+        (void)esch_object_delete(ESCH_CAST_TO_OBJECT(vec_fixed));
+    }
+    if (vec_dyn != NULL) {
+        (void)esch_object_delete(ESCH_CAST_TO_OBJECT(vec_dyn));
+    }
+    /* Set default value. */
+    (void)esch_config_set_int(config, ESCH_CONFIG_KEY_VECTOR_ENLARGE, 0);
+    (void)esch_config_set_int(config, ESCH_CONFIG_KEY_VECTOR_LENGTH, 1);
+    return ret;
+}
+

src/utest/esch_utest.c

     ESCH_TEST_CHECK(ret == ESCH_OK, "test_vectorIteration() failed", ret);
     esch_log_info(g_testLog, "[PASSED] test_vectorIteration()");
 
+    esch_log_info(testLog, "Start: test_vectorResizeFlag()");
+    ret = test_vectorResizeFlag(config);
+    ESCH_TEST_CHECK(ret == ESCH_OK, "test_vectorResizeFlag() failed", ret);
+    esch_log_info(testLog, "[PASSED] test_vectorResizeFlag()");
+
     esch_log_info(testLog, "Start: test_gcCreateDelete()");
     ret = test_gcCreateDelete(config);
     ESCH_TEST_CHECK(ret == ESCH_OK, "test_gcCreateDelete() failed", ret);

src/utest/esch_utest.h

 extern esch_error test_vectorBase(esch_config* config);
 extern esch_error test_vectorElementType(esch_config* config);
 extern esch_error test_vectorIteration(esch_config* config);
+extern esch_error test_vectorResizeFlag(esch_config* config);
 extern esch_error test_integer();
 extern esch_error test_gcCreateDelete(esch_config* config);
 extern esch_error test_gcRecycleLogic(esch_config* config);
   - [DONE] Update GC to make unit test fully pass (esch_vector as gc root).
   - [DONE] Add more test cases to cover multiple scenarios.
   - Add stress to simulate memory exhausting scenario.
-  - Think about it. Do we really need to dynamically expand GC slots?
+  - [DONE] Set GC slots resizing configurable.
 
 * Implement list type (requires GC for verification)
   - Update esch_string to allow iteration.
   - Implement list type with GC.
-  - Think about it. Do we really need to dynamically expand vector?
+  - [DONE] Set vector slots resizing configurable.
 
 * Allow vector store primitive types.