A PHP Error was encountered

Severity: Warning

Message: file_put_contents(): Only 0 of 42 bytes written, possibly out of free disk space

Filename: libraries/article.php

Line Number: 212

A PHP Error was encountered

Severity: Warning

Message: mkdir(): File exists

Filename: libraries/menu.php

Line Number: 500

A PHP Error was encountered

Severity: Warning

Message: file_put_contents(/var/www/html/contents/menus/h_blogs/b_purenode/views/purenode memory and component programming model_0): failed to open stream: No such file or directory

Filename: libraries/menu.php

Line Number: 505

iadix blockchain platform - blogs Purenode memory and component programming model en

Iadix HTML 5.0 multichain technology
Blockchain, economic solutions and online business
A blockchain to link them all



Purenode memory and component programming model

author:BitAdmin (Registered)
Join date:15/01/16 22:18

The purenode blockchain engine rely on a framework made in C to handle low level function such as memory managment, software typing, I/O, and network protocol definition.

It is strongly though with scalability and parallelisation in mind, based on lockless list of event who can be hanlded either as "green threads", where events are processed sequentially in a single thread, or sharing dynamically a list of dynamic object between differents thread, locklessly if only one thread need to modify the data, which can be usefull when an UI or front end need to only read the data, keeping its local copy synchronized and coherent if other thread change this list of object.


The memory model can be though in 3 layers :





  1. Memory area :

    base memory model

    They are initialized for each threads, contain a stack of free zones initialized with a fixed memory size,and keep track of all memory allocated and free areas.


    The implementation of the memory area and the base allocator is found is this file




    The first initialization of the memory system is in this function




    typedef struct memory zone descriptor 
     mem_ptr ptr;pointer to the memory location
     mem_size size;size of the allocated memory
     typedef struct   referenced memory zone definition
     mem_zone_desc mem; memory description
     unsigned int area_id; area id
     unsigned int n_refs; reference counter
     unsigned int time; allocation / used time
     zone_free_func_ptr free_func; custom function on free
     typedef mem_zone *mem_zone_ptr; 
     typedef const mem_zone_desc *mem_zone_desc_ptr; 
     typedef struct  memory area
     unsigned int area_id; id of the area
     mem_area_type_t type; type tree node or plain
     mem_ptr lock_sema; semaphore for multi thread allocation
     mem_ptr area_start; start of area in process memory
     mem_ptr area_end; end of area in process memory
     mem_zone_desc zones_free[MAX_MEM_ZONES]; descriptor for free zones
     mem_zone_ptr zones_buffer; dyanmic buffer of free zones




    OS_API_C_FUNC(void) init_mem_system()initialize memory system
     __global_mem_areas =get_next_aligned_ptr(kernel_memory_map_c(MAX_MEM_AREAS*sizeof(mem_area)+8));allocate memory from the kernel for memory areas structures
     __global_mem_zones =get_next_aligned_ptr(kernel_memory_map_c(MAX_MEM_AREAS*MAX_MEM_ZONES*sizeof(mem_zone)+8));allocate memory from the kernel for available memory zones for each areas
     memset_c(__global_mem_areas,0,MAX_MEM_AREAS*sizeof(mem_area) );initialize memory area pool
     memset_c(__global_mem_zones,0,MAX_MEM_AREAS*MAX_MEM_ZONES*sizeof(mem_zone) );initlialize memory zone pool




    OS_API_C_FUNC(unsigned int)init_new_mem_area(mem_ptr phys_start,mem_ptr phys_end,mem_area_type_t type)initialize new memory area from start to end , type is either tree node or regular memory 
     int n;  
     n = 0;  
     while (!compare_z_exchange_c(&area_lock, 1))if ((n++) >= 1000)return 0; wait for free lock on the global memory where the memory area are stored
     n = 0;  find unused memory area in the pool
     if(__global_mem_areas[n].area_start == 0x00000000)  initialize new memory area from the input parameters
     __global_mem_areas[n].area_start = phys_start; 
     __global_mem_areas[n].area_id =n+1; 
     __global_mem_areas[n].area_end =phys_end; 
     __global_mem_areas[n].type =type; 
     __global_mem_areas[n].zones_buffer =&__global_mem_zones[MAX_MEM_ZONES*n]; assign pointer to zone buffer in the global area
     __global_mem_areas[n].lock_sema =PTR_NULL; 
     memset_c (__global_mem_areas[n].zones_free ,0,MAX_MEM_ZONES*sizeof(mem_zone_desc)); initialize free zones stack
     __global_mem_areas[n].zones_free[0].ptr =get_next_aligned_ptr(__global_mem_areas[n].area_start); initialize initial free zone to the whole avaliable memory for this area
     __global_mem_areas[n].zones_free[0].size =get_aligned_size(__global_mem_areas[n].area_start,__global_mem_areas[n].area_end); 
     area_lock = 0;  release the lock and return
     return __global_mem_areas[n].area_id;  
     n++;  next area
     area_lock = 0; no free area found 
     return 0xFFFFFFFF;  




    OS_API_C_FUNC(unsigned int)allocate_new_zone(unsigned int area_id,mem_size zone_size,mem_zone_ref *zone_ref)  allocate a new zone and output reference to it in zone_ref
     unsigned int n; 
     mem_area *area_ptr; 
     area_ptr =get_area(area_id); find area, or get default memory for the thread
     if(area_ptr==PTR_NULL) return 0; 
     release_zone_ref (zone_ref);  release reference to object in output parameter
     zone_size = ((zone_size&0xFFFFFFF0)+16); 
     n =0; 
      if(area_ptr->zones_buffer[n].mem.ptr ==  PTR_NULL) find available zone in the area buffer
     mem_zone *nzone; 
     nzone = &area_ptr->zones_buffer[n]; 
     if(find_free_zone(area_ptr,zone_size,&nzone->mem)==1)find free zone in the memory pool and assign the memory descriptor in the zone buffer
     if(allocate_zone(area_ptr,&nzone->mem)==1) allocate the zone and reference it to the output reference
     nzone->area_id = area_ptr->area_id; 
     nzone->n_refs = 1; 
     nzone->free_func= PTR_NULL; 
     memset_c (nzone->mem.ptr,0x00,nzone->mem.size); 
     zone_ref-> zone = nzone; 
     return 1; 
      else return 0; allocation failure not enough free memory
     }  no more free zones
     else return 0  
     n++;  next avaiable memory zone
     return 0;  no available new zone found



    The main api to use the base memory allocator is in the file




    LIBC_API void C_API_FUNCinit_default_mem_area (unsigned int size);
    LIBC_API unsigned int C_API_FUNCmem_area_enable_sem (unsigned int area_id); 
    LIBC_API unsigned int C_API_FUNCinit_new_mem_area (mem_ptr phys_start, mem_ptr phys_end,mem_area_type_t type); 
    LIBC_API unsigned int C_API_FUNCfree_mem_area (unsigned int area_id); 
    LIBC_API unsigned int C_API_FUNCallocate_new_zone (unsigned int area_id, mem_size zone_size, mem_zone_ref *zone_ref); 
    LIBC_API unsigned int C_API_FUNCallocate_new_empty_zone (unsigned int area_id,mem_zone_ref *zone_ref); 
    LIBC_API int C_API_FUNCexpand_zone (mem_zone_ref *ref,mem_size new_size); 
    LIBC_API int C_API_FUNCrealloc_zone (mem_zone_ref *zone_ref,mem_size new_size); 
    LIBC_API void C_API_FUNCcopy_zone_ref (mem_zone_ref_ptr dest_zone_ref,mem_zone_ref_const_ptr zone_ref); 
    LIBC_API void C_API_FUNCcopy_zone_const_ref (mem_zone_const_ref_ptr dest_zone_ref,mem_zone_const_ref_ptr zone_ref); 
    LIBC_API unsigned int C_API_FUNCcreate_zone_ref (mem_zone_ref *dest_zone_ref,mem_ptr ptr,mem_size size); 
    LIBC_API void C_API_FUNCinit_mem_system (); 
    LIBC_API void C_API_FUNCdump_mem_used (unsigned int area_id); 
    LIBC_API void C_API_FUNCdump_mem_used_after (unsigned int area_id,unsigned int time); 
    LIBC_API mem_ptr C_API_FUNCget_zone_ptr (mem_zone_ref_const_ptr ref,mem_size ofset); 
    LIBC_API mem_size C_API_FUNCget_zone_size (mem_zone_ref_const_ptr ref); 
    LIBC_API unsigned int C_API_FUNCfind_zones_used (unsigned int area_id); 
    LIBC_API unsigned int C_API_FUNCget_zone_numref (mem_zone_ref *zone_ref); 
    LIBC_API void C_API_FUNCswap_zone_ref (mem_zone_ref_ptr dest_zone_ref, mem_zone_ref_ptr src_zone_ref); 
    LIBC_API int C_API_FUNCalign_zone_memory (mem_zone_ref *zone_ref, mem_size align); 
    LIBC_API int C_API_FUNCset_mem_area_id (unsigned int area_id); 
    LIBC_API int C_API_FUNCset_tree_mem_area_id (unsigned int area_id); 
    LIBC_API unsigned int C_API_FUNCget_mem_area_id (); 
    LIBC_API unsigned int C_API_FUNCget_tree_mem_area_id (); 



    An example initialization of the memory area system is found in the launcher executable 






    int main(int argc, char **argv)
     mem_zone_ref params = { PTR_NULL };
     mem_ptr *params_ptr;
     int done = 0,n;
     init_mem_system ();
     init_default_mem_area (8 * 1024 * 1024);
     set_exe_path ();
     if (!set_home_path("purenode")) 
     console_print("could not set home dir 'purenode' \n");
     return 0;
     load_module("modz/libbase.tpo", "libbase", &libbase_mod);
     load_module("modz/protocol_adx.tpo", "protocol_adx", &protocol_mod);
     load_module("modz/block_adx.tpo", "block_adx", &block_mod);
     load_module("modz/iadixcoin.tpo", "iadixcoin", &iadix_mod);
     app_init = (app_func_ptr)get_tpo_mod_exp_addr_name(&iadix_mod, "app_init", 0);
     app_start = (app_func_ptr)get_tpo_mod_exp_addr_name(&iadix_mod, "app_start", 0);
     app_loop = (app_func_ptr)get_tpo_mod_exp_addr_name(&iadix_mod, "app_loop", 0);
     app_stop = (app_func_ptr)get_tpo_mod_exp_addr_name(&iadix_mod, "app_stop", 0);
     if (!app_init((mem_zone_ref_ptr)PTR_NULL)) 
     console_print("could not initialize app ");
     return 0;
     if (daemonize("purenode") <= 0) 
     console_print("daemonize failed \n");
     return 0;
     if (argc > 1) 
     allocate_new_zone(0, argc*sizeof(mem_ptr),¶ms);
     for (n = 0; n < (argc-1); n++) 
     params_ptr = get_zone_ptr(¶ms, n*sizeof(mem_ptr));
     (*params_ptr) = argv[n+1];
     params_ptr =get_zone_ptr(¶ms, n*sizeof(mem_ptr));
     (*params_ptr) = PTR_NULL;
     if (!app_start(¶ms)) 
     console_print("could not start app ");
     return 0;
     while (isRunning()) 



    The equivalent for the stdc allocation style is found in the file :



    The string api is an example of higher level use of the allocation system



  2. Dynamic Tree System Runtime

    tree memory model

    The base api to access the tree node system is defined in this file :


    Example of tree system to decode and parse blockchain protocol message based on json hard-typed templates:




  3. Application layer 

    application module model

    The message processing in the blockchain node application can show an example of high level management of the dynamic tree with reference pointers.


    int process_node_messages(mem_zone_ref_ptr node)
     mem_zone_ref msg_list = { PTR_NULL };
     mem_zone_ref my_list = { PTR_NULL };
     mem_zone_ref_ptr msg = PTR_NULL;
     if (!tree_manager_find_child_node(node, NODE_HASH("emitted queue"), NODE_BITCORE_MSG_LIST, &msg_list))return 0;
     for (tree_manager_get_first_child(&msg_list, &my_list, &msg); ((msg != NULL) && (msg->zone != NULL)); tree_manager_get_next_child(&my_list, &msg))
     char cmd[16];
     mem_zone_ref payload_node = { PTR_NULL };
     int ret;
     if (!tree_manager_get_child_value_str(msg, NODE_HASH("cmd") , cmd, 12, 16))continue;
     tree_manager_find_child_node(msg, NODE_HASH("payload"), NODE_BITCORE_PAYLOAD, &payload_node);
     ret = handle_message(node, cmd, &payload_node);
     tree_manager_set_child_value_i32(msg, "handled", ret);
     tree_remove_child_by_member_value_dword (&msg_list, NODE_BITCORE_MSG, "handled", 1); 
     tree_remove_child_by_member_value_lt_dword (&msg_list, NODE_BITCORE_MSG, "recvtime", get_time_c()-100); 
     return 1; 


    Message handling in the node application code


    int handle_message(mem_zone_ref_ptr node,const char *cmd,mem_zone_ref_ptr payload)
    unsigned int testing;
    if (!strncmp_c(cmd, "headers", 7))return handle_headers(node, payload);
    if (!strncmp_c(cmd, "block", 5))return handle_block(node, payload);
    if (!strncmp_c(cmd, "inv", 3))return handle_inv(node, payload);
    if (!tree_manager_get_child_value_i32(node, NODE_HASH("testing_chain"), &testing))
     testing = 0;
    if (testing > 0)return 1; 
    if (!strncmp_c(cmd, "verack", 6))return handle_verack(node, payload);
    if (!strncmp_c(cmd, "version", 7))return handle_version(node, payload);
    if (!strncmp_c(cmd, "ping", 4))return handle_ping(node, payload);
    if (!strncmp_c(cmd, "pong", 4))return handle_pong(node, payload);
    if (!strncmp_c(cmd, "addr", 4))return handle_addr(node, payload);
    if (!strncmp_c(cmd, "getdata", 7))return handle_getdata(node, payload);
    return 0; 


    High level implementation of the block explorer http request can be seen in this file :




    OS_API_C_FUNC(intblocks(const char *params, const struct http_req *req, mem_zone_ref_ptr result) 
     hash_t nullhash;
     char chash[65], prm[65];
     mem_zone_ref time_index_node = { PTR_NULL }, block_index_node = { PTR_NULL }, new_block = { PTR_NULL }, block_list = { PTR_NULL };
     struct string blk_path = { PTR_NULL };
     ctime_t time;
     uint64_t nblks=0;
     size_t page_num;
     unsigned int block_time, limit, num, idx,tidx,n,start_time,cur_time;
     const struct key_val *blockdate, *pageNum, *sinceblock, *beforeblock, *txl;
     tree_manager_find_child_node (&my_node, NODE_HASH("block index"), NODE_BITCORE_HASH, &block_index_node);
     tree_manager_find_child_node (&my_node, NODE_HASH("block time"), NODE_GFX_INT, &time_index_node);
     tree_manager_get_child_value_i64 (&my_node, NODE_HASH("block height"), &nblks);
     memset_c(nullhash, 0sizeof(hash_t));
     if ((pageNum = find_key(req->query_vars, "pageNum")) != PTR_NULL)
     page_num =strtoul_c(pageNum->value.str,PTR_NULL,10);
     page_num =0;
     if (isdigit_c(params[0])) 
     limit = strtoul_c(params, PTR_NULL, 10);
     limit =0;
     if ((limit < 1) || (limit > 10)) 
     limit=  10;
     txl =find_key(req->query_vars, "tx");
     tree_manager_add_child_node(result, "blocks", NODE_JSON_ARRAY, &block_list);
     if ((blockdate = find_key(req->query_vars, "BlockDate")) != PTR_NULL)
     ctime_t next_day;
     time = parseDate(blockdate->value.str);
     block_time = 0xFFFFFFFF;
     next_day =time + 24 * 3600;
     idx = nblks;
     while ((--idx) > 1) 
     if (!tree_mamanger_get_node_dword(&time_index_node, (idx) * 4, &block_time))break;
     if (block_time < next_day)break;
     if (idx <= 1) 
     return 1; 
     num =0;
     tidx =0;
     while ((block_time >= time)&&((idx--)>0)) 
     if (num < limit) 
     if (tree_manager_create_node("block", NODE_GFX_OBJECT, &new_block)) 
     tree_manager_get_node_str(&block_index_node, idx * 32, chash, 65, 16);
     n = 0;
     while (n < 32) 
     prm[(31 - n) * 2 + 0] = chash[n * 2 + 0];
     prm[(31 - n) * 2 + 1] = chash[n * 2 + 1];
     prm[64] = 0;
     if (block(prm, req, &new_block)) 
     if (tidx >= page_num*limit) 
     tree_manager_node_add_child(&block_list, &new_block);
     if (!tree_mamanger_get_node_dword(&time_index_node, idx * 4, &block_time))
     block_time = 0;
     tree_manager_set_child_value_i32(result, "limit", limit);
     tree_manager_set_child_value_i32(result, "page_num"page_num);
     tree_manager_set_child_value_i32(result, "numblocks", tidx);

    Implementation of the rpc server request can be seen in this file :



    OS_API_C_FUNC(int)  listunspent(mem_zone_ref_const_ptr params, unsigned int rpc_mode, mem_zone_ref_ptr result) 
     mem_zone_ref minconf = { PTR_NULL }, maxconf = { PTR_NULL }, unspents = { PTR_NULL }, addrs = { PTR_NULL }; 
     mem_zone_ref my_list = { PTR_NULL }; 
     mem_zone_ref_ptr addr; 
     uint64_t total=0; 
     size_t min_conf = 0, max_conf = 9999; 
     size_t max = 200,ntx=0; 
     if (!tree_manager_add_child_node(result,"unspents", NODE_JSON_ARRAY, &unspents)) 
     return 0; 
     tree_manager_get_child_at(params, 0, &minconf); 
     tree_manager_get_child_at(params, 1, &maxconf); 
     tree_manager_get_child_at(params, 2, &addrs); 
     tree_mamanger_get_node_dword(&minconf, 0, &min_conf); 
     tree_mamanger_get_node_dword(&maxconf, 0, &max_conf); 
     for (tree_manager_get_first_child(&addrs, &my_list, &addr); ((addr != NULL) && (addr->zone != NULL)); tree_manager_get_next_child(&my_list, &addr)) 
     btc_addr_t my_addr; 
     tree_manager_get_node_btcaddr (addr, 0, my_addr); 
     list_unspent (my_addr, &unspents, min_conf, max_conf, &total, &ntx, &max); 
     tree_manager_set_child_value_i64(result, "ntx", ntx); 
     tree_manager_set_child_value_i64(result, "total",total ); 
     release_zone_ref (&addrs); 
     release_zone_ref (&unspents); 
     return 1;