Modest/source/modest/finder/thread.c

490 lines
17 KiB
C

/*
Copyright (C) 2016-2017 Alexander Borisov
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either
version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public
License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
Author: lex.borisov@gmail.com (Alexander Borisov)
*/
#include "modest/finder/thread.h"
/* private functions */
#ifndef MyCORE_BUILD_WITHOUT_THREADS
static void modest_finder_thread_stream(mythread_id_t thread_id, void* arg);
#else
static void modest_finder_thread_stream_single(modest_finder_thread_t* finder_thread, mycss_selectors_list_t* selector_list);
#endif
static modest_finder_thread_context_t * modest_finder_thread_create_context(modest_finder_thread_t* finder_thread, size_t count);
//static void modest_finder_thread_callback_found(modest_finder_t* finder, myhtml_tree_node_t* node, mycss_selectors_list_t* selector_list,
// mycss_selectors_entry_t* selector, mycss_selectors_specificity_t* spec, void* ctx);
/* basic functions */
modest_finder_thread_t * modest_finder_thread_create(void)
{
return (modest_finder_thread_t*)mycore_calloc(1, sizeof(modest_finder_thread_t));
}
mystatus_t modest_finder_thread_init(modest_finder_t* finder, modest_finder_thread_t* finder_thread, size_t thread_count)
{
#ifdef MyCORE_BUILD_WITHOUT_THREADS
thread_count = 1;
#endif
finder_thread->finder = finder;
/* objects for nodes */
finder_thread->entry_obj = mcobject_async_create();
if(finder_thread->entry_obj == NULL)
return MODEST_STATUS_OK;
mcobject_async_status_t mcstatus = mcobject_async_init(finder_thread->entry_obj, 128, 1024, sizeof(modest_finder_thread_entry_t));
if(mcstatus)
return MODEST_STATUS_OK;
/* objects for declarations */
finder_thread->declaration_obj = mcobject_async_create();
if(finder_thread->declaration_obj == NULL)
return MODEST_STATUS_OK;
mcstatus = mcobject_async_init(finder_thread->declaration_obj, 128, 1024, sizeof(modest_finder_thread_declaration_t));
if(mcstatus)
return MODEST_STATUS_OK;
finder_thread->context_list = modest_finder_thread_create_context(finder_thread, thread_count);
if(finder_thread->context_list == NULL)
return MODEST_STATUS_OK;
/* create and init threads */
#ifdef MyCORE_BUILD_WITHOUT_THREADS
finder_thread->thread = NULL;
#else
finder_thread->thread = mythread_create();
if(finder_thread->thread == NULL)
return MODEST_STATUS_OK;
mystatus_t status = mythread_init(finder_thread->thread, MyTHREAD_TYPE_STREAM, thread_count, 0);
if(status) {
mythread_destroy(finder_thread->thread, NULL, NULL, true);
return MODEST_STATUS_OK;
}
finder_thread->thread->context = finder_thread;
/* create threads */
for(size_t i = 0; i < finder_thread->thread->entries_size; i++) {
myhread_entry_create(finder_thread->thread, mythread_function, modest_finder_thread_stream, MyTHREAD_OPT_STOP);
}
#endif
return MODEST_STATUS_OK;
}
void modest_finder_thread_clean(modest_finder_thread_t* finder_thread, bool self_destroy)
{
for(size_t i = 1; i < finder_thread->context_list_size; i++) {
mcobject_async_node_clean(finder_thread->entry_obj, finder_thread->context_list[i].entry_node_id);
mcobject_async_node_clean(finder_thread->declaration_obj, finder_thread->context_list[i].declaration_node_id);
}
}
modest_finder_thread_t * modest_finder_thread_destroy(modest_finder_thread_t* finder_thread, bool self_destroy)
{
if(finder_thread == NULL)
return NULL;
#ifndef MyCORE_BUILD_WITHOUT_THREADS
if(finder_thread->thread) {
finder_thread->thread = mythread_destroy(finder_thread->thread, mythread_callback_quit, NULL, true);
}
#endif
finder_thread->entry_obj = mcobject_async_destroy(finder_thread->entry_obj, true);
finder_thread->declaration_obj = mcobject_async_destroy(finder_thread->declaration_obj, true);
if(finder_thread->context_list) {
mycore_free(finder_thread->context_list);
finder_thread->context_list = NULL;
finder_thread->context_list_size = 0;
}
if(self_destroy) {
mycore_free(finder_thread);
return NULL;
}
return finder_thread;
}
void modest_finder_thread_collate_node(modest_t* modest, myhtml_tree_node_t* node, modest_finder_thread_entry_t* entry)
{
modest_finder_thread_declaration_t* dec = entry->declaration;
while(dec) {
if(dec->entry)
modest_style_map_collate_declaration(modest, node, dec->entry, dec->entry->type, &dec->raw_spec);
dec = dec->next;
}
}
#ifdef MyCORE_BUILD_WITHOUT_THREADS
mystatus_t modest_finder_thread_process(modest_t* modest, modest_finder_thread_t* finder_thread,
myhtml_tree_node_t* scope_node, mycss_selectors_list_t* selector_list)
{
finder_thread->base_node = scope_node;
finder_thread->selector_list = selector_list;
if(finder_thread->finder == NULL)
return MODEST_STATUS_ERROR;
modest_finder_thread_stream_single(finder_thread, selector_list);
/* calc result */
modest_finder_thread_context_t* context = finder_thread->context_list;
myhtml_tree_node_t* node = scope_node;
/* compare results */
while(node) {
modest_finder_thread_entry_t* entry = context->entry;
while(entry) {
if(entry->node == node)
{
if(entry->next)
entry->next->prev = entry->prev;
else
context->entry_last = entry->prev;
if(entry->prev)
entry->prev->next = entry->next;
else
context->entry = entry->next;
modest_finder_thread_collate_node(modest, node, entry);
}
entry = entry->next;
}
if(node->child)
node = node->child;
else {
while(node != scope_node && node->next == NULL)
node = node->parent;
if(node == scope_node)
break;
node = node->next;
}
}
return MyCORE_STATUS_OK;
}
#else /* end def MyCORE_BUILD_WITHOUT_THREADS */
mystatus_t modest_finder_thread_process(modest_t* modest, modest_finder_thread_t* finder_thread,
myhtml_tree_node_t* scope_node, mycss_selectors_list_t* selector_list)
{
finder_thread->base_node = scope_node;
finder_thread->selector_list = selector_list;
if(finder_thread->finder == NULL)
return MODEST_STATUS_ERROR;
mythread_resume(finder_thread->thread, MyTHREAD_OPT_UNDEF);
modest_finder_thread_wait_for_all_done(finder_thread);
/* calc result */
modest_finder_thread_context_t* context_list = finder_thread->context_list;
myhtml_tree_node_t* node = scope_node;
/* compare results */
while(node) {
for(size_t i = 0; i < finder_thread->thread->entries_length; i++)
{
modest_finder_thread_context_t* context = &context_list[i];
modest_finder_thread_entry_t* entry = context->entry;
while(entry) {
if(entry->node == node)
{
if(entry->next)
entry->next->prev = entry->prev;
else
context->entry_last = entry->prev;
if(entry->prev)
entry->prev->next = entry->next;
else
context->entry = entry->next;
modest_finder_thread_collate_node(modest, node, entry);
}
entry = entry->next;
}
}
if(node->child)
node = node->child;
else {
while(node != scope_node && node->next == NULL)
node = node->parent;
if(node == scope_node)
break;
node = node->next;
}
}
return MODEST_STATUS_OK;
}
void modest_finder_thread_wait_for_all_done(modest_finder_thread_t* finder_thread)
{
for (size_t idx = 0; idx < finder_thread->thread->entries_length; idx++) {
while((finder_thread->thread->entries[idx].context.opt & MyTHREAD_OPT_DONE) == 0) {
mythread_nanosleep_sleep(finder_thread->thread->timespec);
}
}
}
#endif /* if undef MyCORE_BUILD_WITHOUT_THREADS */
modest_finder_thread_context_t * modest_finder_thread_create_context(modest_finder_thread_t* finder_thread, size_t count)
{
finder_thread->context_list_size = count;
modest_finder_thread_context_t *ctx = mycore_calloc(count, sizeof(modest_finder_thread_context_t));
if(ctx == NULL)
return NULL;
mcobject_async_status_t mcstatus;
for(size_t i = 0; i < count; i++) {
ctx[i].entry_node_id = mcobject_async_node_add(finder_thread->entry_obj, &mcstatus);
if(mcstatus) {
while(i) {
i--;
mcobject_async_node_delete(finder_thread->entry_obj, ctx[i].entry_node_id);
}
mycore_free(ctx);
return NULL;
}
}
for(size_t i = 0; i < count; i++) {
ctx[i].declaration_node_id = mcobject_async_node_add(finder_thread->declaration_obj, &mcstatus);
if(mcstatus) {
size_t t = count;
while(t > 1) {
t--;
mcobject_async_node_delete(finder_thread->entry_obj, ctx[t].entry_node_id);
}
while(i > 1) {
i--;
mcobject_async_node_delete(finder_thread->declaration_obj, ctx[i].declaration_node_id);
}
mycore_free(ctx);
return NULL;
}
}
return ctx;
}
bool modest_finder_thread_spec_is_up(modest_style_raw_specificity_t* spec_f, modest_style_raw_specificity_t* spec_t)
{
if(spec_f->x > spec_t->x)
return true;
else if(spec_f->x < spec_t->x)
return false;
if(spec_f->a > spec_t->a)
return true;
else if(spec_f->a < spec_t->a)
return false;
if(spec_f->b > spec_t->b)
return true;
else if(spec_f->b < spec_t->b)
return false;
if(spec_f->c > spec_t->c)
return true;
else if(spec_f->c < spec_t->c)
return false;
/* when a property is repeated with multiple values take the last one*/
return true;
}
void modest_finder_thread_declaratin_append(modest_finder_thread_found_context_t* found_context, bool is_low_priority,
modest_finder_thread_entry_t* entry, mycss_declaration_entry_t* dec_entry,
modest_style_raw_specificity_t* raw_spec)
{
if(entry->declaration == NULL) {
entry->declaration = entry->declaration_last = mcobject_async_malloc(found_context->finder_thread->declaration_obj,
found_context->context->declaration_node_id, NULL);
entry->declaration->entry = dec_entry;
entry->declaration->raw_spec = *raw_spec;
entry->declaration->next = NULL;
entry->declaration->prev = NULL;
return;
}
modest_finder_thread_declaration_t* thr_dec = entry->declaration;
while(thr_dec) {
if(thr_dec->entry->type == dec_entry->type)
{
if(modest_finder_thread_spec_is_up(raw_spec, &thr_dec->raw_spec)) {
thr_dec->entry = dec_entry;
thr_dec->raw_spec = *raw_spec;
}
return;
}
thr_dec = thr_dec->next;
}
thr_dec = mcobject_async_malloc(found_context->finder_thread->declaration_obj,
found_context->context->declaration_node_id, NULL);
thr_dec->next = NULL;
thr_dec->entry = dec_entry;
thr_dec->raw_spec = *raw_spec;
entry->declaration_last->next = thr_dec;
thr_dec->prev = entry->declaration_last;
entry->declaration_last = thr_dec;
}
void modest_finder_thread_declaratin_list_replace(modest_finder_thread_found_context_t* found_context,
modest_finder_thread_entry_t* entry, mycss_declaration_entry_t* dec_entry,
mycss_selectors_specificity_t* spec)
{
while(dec_entry) {
modest_style_raw_specificity_t raw_spec = {((unsigned int)dec_entry->is_important), spec->a, spec->b, spec->c};
modest_finder_thread_declaratin_append(found_context, false, entry, dec_entry, &raw_spec);
dec_entry = dec_entry->next;
}
}
void modest_finder_thread_callback_found(modest_finder_t* finder, myhtml_tree_node_t* node, mycss_selectors_list_t* selector_list, mycss_selectors_entry_t* selector, mycss_selectors_specificity_t* spec, void* ctx)
{
modest_finder_thread_found_context_t* found_context = (modest_finder_thread_found_context_t*)ctx;
modest_finder_thread_context_t* thread_context = found_context->context;
if(thread_context->entry_last) {
modest_finder_thread_entry_t* entry = thread_context->entry;
while(entry)
{
if(entry->node == node) {
modest_finder_thread_declaratin_list_replace(found_context, entry, selector_list->declaration_entry, spec);
return;
}
entry = entry->next;
}
}
modest_finder_thread_entry_t* entry = mcobject_async_malloc(found_context->finder_thread->entry_obj, thread_context->entry_node_id, NULL);
memset(entry, 0, sizeof(modest_finder_thread_entry_t));
entry->node = node;
modest_finder_thread_declaratin_list_replace(found_context, entry, selector_list->declaration_entry, spec);
if(thread_context->entry_last) {
entry->prev = thread_context->entry_last;
thread_context->entry_last->next = entry;
thread_context->entry_last = entry;
}
else {
thread_context->entry_last = thread_context->entry = entry;
}
}
void modest_finder_thread_stream_single(modest_finder_thread_t* finder_thread, mycss_selectors_list_t* selector_list)
{
modest_finder_thread_found_context_t found_ctx = {finder_thread, finder_thread->context_list};
while(selector_list)
{
for(size_t i = 0; i < selector_list->entries_list_length; i++) {
mycss_selectors_entries_list_t *entries = &selector_list->entries_list[i];
mycss_selectors_specificity_t spec = entries->specificity;
modest_finder_node_combinator_begin(finder_thread->finder, finder_thread->base_node, selector_list,
entries->entry, &spec, modest_finder_thread_callback_found, &found_ctx);
}
selector_list = selector_list->next;
}
}
#ifndef MyCORE_BUILD_WITHOUT_THREADS
void modest_finder_thread_stream(mythread_id_t thread_id, void* arg)
{
mythread_context_t* ctx = (mythread_context_t*)arg;
modest_finder_thread_t* finder_thread = (modest_finder_thread_t*)ctx->mythread->context;
mycss_selectors_list_t* selector_list = finder_thread->selector_list;
modest_finder_thread_found_context_t found_ctx = {finder_thread, &finder_thread->context_list[ctx->id]};
size_t count = 0, counnt_done = ctx->id - 1;
size_t offset = (ctx->mythread->entries_size - 1);
while(selector_list) {
for(size_t i = 0; i < selector_list->entries_list_length; i++) {
/* split selectors by thread id */
if(count == counnt_done) {
mycss_selectors_entries_list_t *entries = &selector_list->entries_list[i];
mycss_selectors_specificity_t spec = entries->specificity;
modest_finder_node_combinator_begin(finder_thread->finder, finder_thread->base_node, selector_list,
entries->entry, &spec, modest_finder_thread_callback_found, &found_ctx);
counnt_done += offset;
}
count++;
}
selector_list = selector_list->next;
}
}
#endif