1 /*
2  Copyright (C) 2016-2017 Alexander Borisov
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  You should have received a copy of the GNU Lesser General Public
15  License along with this library; if not, write to the Free Software
16  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
17 
18  Author: lex.borisov@gmail.com (Alexander Borisov)
19 */
20 
21 #include "modest/finder/thread.h"
22 
23 /* private functions */
24 #ifndef MyCORE_BUILD_WITHOUT_THREADS
25 static void modest_finder_thread_stream(mythread_id_t thread_id, void* arg);
26 #else
27 static void modest_finder_thread_stream_single(modest_finder_thread_t* finder_thread, mycss_selectors_list_t* selector_list);
28 #endif
29 
30 static modest_finder_thread_context_t * modest_finder_thread_create_context(modest_finder_thread_t* finder_thread, size_t count);
31 //static void modest_finder_thread_callback_found(modest_finder_t* finder, myhtml_tree_node_t* node, mycss_selectors_list_t* selector_list,
32 //                                                mycss_selectors_entry_t* selector, mycss_selectors_specificity_t* spec, void* ctx);
33 
34 /* basic functions */
modest_finder_thread_create(void)35 modest_finder_thread_t * modest_finder_thread_create(void)
36 {
37     return (modest_finder_thread_t*)mycore_calloc(1, sizeof(modest_finder_thread_t));
38 }
39 
modest_finder_thread_init(modest_finder_t * finder,modest_finder_thread_t * finder_thread,size_t thread_count)40 mystatus_t modest_finder_thread_init(modest_finder_t* finder, modest_finder_thread_t* finder_thread, size_t thread_count)
41 {
42 #ifdef MyCORE_BUILD_WITHOUT_THREADS
43     thread_count = 1;
44 #endif
45 
46     finder_thread->finder = finder;
47 
48     /* objects for nodes */
49     finder_thread->entry_obj = mcobject_async_create();
50     if(finder_thread->entry_obj == NULL)
51         return MODEST_STATUS_OK;
52 
53     mcobject_async_status_t mcstatus = mcobject_async_init(finder_thread->entry_obj, 128, 1024, sizeof(modest_finder_thread_entry_t));
54     if(mcstatus)
55         return MODEST_STATUS_OK;
56 
57     /* objects for declarations */
58     finder_thread->declaration_obj = mcobject_async_create();
59     if(finder_thread->declaration_obj == NULL)
60         return MODEST_STATUS_OK;
61 
62     mcstatus = mcobject_async_init(finder_thread->declaration_obj, 128, 1024, sizeof(modest_finder_thread_declaration_t));
63     if(mcstatus)
64         return MODEST_STATUS_OK;
65 
66     finder_thread->context_list = modest_finder_thread_create_context(finder_thread, thread_count);
67     if(finder_thread->context_list == NULL)
68         return MODEST_STATUS_OK;
69 
70     /* create and init threads */
71 #ifdef MyCORE_BUILD_WITHOUT_THREADS
72     finder_thread->thread = NULL;
73 #else
74     finder_thread->thread = mythread_create();
75 
76     if(finder_thread->thread == NULL)
77         return MODEST_STATUS_OK;
78 
79     mystatus_t status = mythread_init(finder_thread->thread, MyTHREAD_TYPE_STREAM, thread_count, 0);
80     if(status) {
81         mythread_destroy(finder_thread->thread, NULL, NULL, true);
82         return MODEST_STATUS_OK;
83     }
84 
85     finder_thread->thread->context = finder_thread;
86 
87     /* create threads */
88     for(size_t i = 0; i < finder_thread->thread->entries_size; i++) {
89         myhread_entry_create(finder_thread->thread, mythread_function, modest_finder_thread_stream, MyTHREAD_OPT_STOP);
90     }
91 #endif
92 
93     return MODEST_STATUS_OK;
94 }
95 
modest_finder_thread_clean(modest_finder_thread_t * finder_thread,bool self_destroy)96 void modest_finder_thread_clean(modest_finder_thread_t* finder_thread, bool self_destroy)
97 {
98     for(size_t i = 1; i < finder_thread->context_list_size; i++) {
99         mcobject_async_node_clean(finder_thread->entry_obj, finder_thread->context_list[i].entry_node_id);
100         mcobject_async_node_clean(finder_thread->declaration_obj, finder_thread->context_list[i].declaration_node_id);
101     }
102 }
103 
modest_finder_thread_destroy(modest_finder_thread_t * finder_thread,bool self_destroy)104 modest_finder_thread_t * modest_finder_thread_destroy(modest_finder_thread_t* finder_thread, bool self_destroy)
105 {
106     if(finder_thread == NULL)
107         return NULL;
108 
109 #ifndef MyCORE_BUILD_WITHOUT_THREADS
110     if(finder_thread->thread) {
111         finder_thread->thread = mythread_destroy(finder_thread->thread, mythread_callback_quit, NULL, true);
112     }
113 #endif
114 
115     finder_thread->entry_obj = mcobject_async_destroy(finder_thread->entry_obj, true);
116     finder_thread->declaration_obj = mcobject_async_destroy(finder_thread->declaration_obj, true);
117 
118     if(finder_thread->context_list) {
119         mycore_free(finder_thread->context_list);
120 
121         finder_thread->context_list = NULL;
122         finder_thread->context_list_size = 0;
123     }
124 
125     if(self_destroy) {
126         mycore_free(finder_thread);
127         return NULL;
128     }
129 
130     return finder_thread;
131 }
132 
modest_finder_thread_collate_node(modest_t * modest,myhtml_tree_node_t * node,modest_finder_thread_entry_t * entry)133 void modest_finder_thread_collate_node(modest_t* modest, myhtml_tree_node_t* node, modest_finder_thread_entry_t* entry)
134 {
135     modest_finder_thread_declaration_t* dec = entry->declaration;
136 
137     while(dec) {
138         if(dec->entry)
139             modest_style_map_collate_declaration(modest, node, dec->entry, dec->entry->type, &dec->raw_spec);
140 
141         dec = dec->next;
142     }
143 }
144 
145 #ifdef MyCORE_BUILD_WITHOUT_THREADS
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)146 mystatus_t modest_finder_thread_process(modest_t* modest, modest_finder_thread_t* finder_thread,
147                                         myhtml_tree_node_t* scope_node, mycss_selectors_list_t* selector_list)
148 {
149     finder_thread->base_node = scope_node;
150     finder_thread->selector_list = selector_list;
151 
152     if(finder_thread->finder == NULL)
153         return MODEST_STATUS_ERROR;
154 
155     modest_finder_thread_stream_single(finder_thread, selector_list);
156 
157     /* calc result */
158     modest_finder_thread_context_t* context = finder_thread->context_list;
159     myhtml_tree_node_t* node = scope_node;
160 
161     /* compare results */
162     while(node) {
163         modest_finder_thread_entry_t* entry = context->entry;
164 
165         while(entry) {
166             if(entry->node == node)
167             {
168                 if(entry->next)
169                     entry->next->prev = entry->prev;
170                 else
171                     context->entry_last = entry->prev;
172 
173                 if(entry->prev)
174                     entry->prev->next = entry->next;
175                 else
176                     context->entry = entry->next;
177 
178                 modest_finder_thread_collate_node(modest, node, entry);
179             }
180 
181             entry = entry->next;
182         }
183 
184         if(node->child)
185             node = node->child;
186         else {
187             while(node != scope_node && node->next == NULL)
188                 node = node->parent;
189 
190             if(node == scope_node)
191                 break;
192 
193             node = node->next;
194         }
195     }
196 
197     return MyCORE_STATUS_OK;
198 }
199 
200 #else /* end def MyCORE_BUILD_WITHOUT_THREADS */
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)201 mystatus_t modest_finder_thread_process(modest_t* modest, modest_finder_thread_t* finder_thread,
202                                         myhtml_tree_node_t* scope_node, mycss_selectors_list_t* selector_list)
203 {
204     finder_thread->base_node = scope_node;
205     finder_thread->selector_list = selector_list;
206 
207     if(finder_thread->finder == NULL)
208         return MODEST_STATUS_ERROR;
209 
210     mythread_resume(finder_thread->thread, MyTHREAD_OPT_UNDEF);
211     modest_finder_thread_wait_for_all_done(finder_thread);
212 
213     /* calc result */
214     modest_finder_thread_context_t* context_list = finder_thread->context_list;
215     myhtml_tree_node_t* node = scope_node;
216 
217     /* compare results */
218     while(node) {
219         for(size_t i = 0; i < finder_thread->thread->entries_length; i++)
220         {
221             modest_finder_thread_context_t* context = &context_list[i];
222             modest_finder_thread_entry_t* entry = context->entry;
223 
224             while(entry) {
225                 if(entry->node == node)
226                 {
227                     if(entry->next)
228                         entry->next->prev = entry->prev;
229                     else
230                         context->entry_last = entry->prev;
231 
232                     if(entry->prev)
233                         entry->prev->next = entry->next;
234                     else
235                         context->entry = entry->next;
236 
237                     modest_finder_thread_collate_node(modest, node, entry);
238                 }
239 
240                 entry = entry->next;
241             }
242         }
243 
244         if(node->child)
245             node = node->child;
246         else {
247             while(node != scope_node && node->next == NULL)
248                 node = node->parent;
249 
250             if(node == scope_node)
251                 break;
252 
253             node = node->next;
254         }
255     }
256 
257     return MODEST_STATUS_OK;
258 }
259 
modest_finder_thread_wait_for_all_done(modest_finder_thread_t * finder_thread)260 void modest_finder_thread_wait_for_all_done(modest_finder_thread_t* finder_thread)
261 {
262     for (size_t idx = 0; idx < finder_thread->thread->entries_length; idx++) {
263         while((finder_thread->thread->entries[idx].context.opt & MyTHREAD_OPT_DONE) == 0) {
264             mythread_nanosleep_sleep(finder_thread->thread->timespec);
265         }
266     }
267 }
268 #endif /* if undef MyCORE_BUILD_WITHOUT_THREADS */
269 
modest_finder_thread_create_context(modest_finder_thread_t * finder_thread,size_t count)270 modest_finder_thread_context_t * modest_finder_thread_create_context(modest_finder_thread_t* finder_thread, size_t count)
271 {
272     finder_thread->context_list_size = count;
273 
274     modest_finder_thread_context_t *ctx = mycore_calloc(count, sizeof(modest_finder_thread_context_t));
275 
276     if(ctx == NULL)
277         return NULL;
278 
279     mcobject_async_status_t mcstatus;
280 
281     for(size_t i = 0; i < count; i++) {
282         ctx[i].entry_node_id = mcobject_async_node_add(finder_thread->entry_obj, &mcstatus);
283 
284         if(mcstatus) {
285             while(i) {
286                 i--;
287                 mcobject_async_node_delete(finder_thread->entry_obj, ctx[i].entry_node_id);
288             }
289 
290             mycore_free(ctx);
291             return NULL;
292         }
293     }
294 
295     for(size_t i = 0; i < count; i++) {
296         ctx[i].declaration_node_id = mcobject_async_node_add(finder_thread->declaration_obj, &mcstatus);
297 
298         if(mcstatus) {
299             size_t t = count;
300             while(t > 1) {
301                 t--;
302                 mcobject_async_node_delete(finder_thread->entry_obj, ctx[t].entry_node_id);
303             }
304 
305             while(i > 1) {
306                 i--;
307                 mcobject_async_node_delete(finder_thread->declaration_obj, ctx[i].declaration_node_id);
308             }
309 
310             mycore_free(ctx);
311             return NULL;
312         }
313     }
314 
315     return ctx;
316 }
317 
modest_finder_thread_spec_is_up(modest_style_raw_specificity_t * spec_f,modest_style_raw_specificity_t * spec_t)318 bool modest_finder_thread_spec_is_up(modest_style_raw_specificity_t* spec_f, modest_style_raw_specificity_t* spec_t)
319 {
320     if(spec_f->x > spec_t->x)
321         return true;
322     else if(spec_f->x < spec_t->x)
323         return false;
324 
325     if(spec_f->a > spec_t->a)
326         return true;
327     else if(spec_f->a < spec_t->a)
328         return false;
329 
330     if(spec_f->b > spec_t->b)
331         return true;
332     else if(spec_f->b < spec_t->b)
333         return false;
334 
335     if(spec_f->c > spec_t->c)
336         return true;
337     else if(spec_f->c < spec_t->c)
338         return false;
339 
340     /* when a property is repeated with multiple values take the last one*/
341     return true;
342 }
343 
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)344 void modest_finder_thread_declaratin_append(modest_finder_thread_found_context_t* found_context, bool is_low_priority,
345                                             modest_finder_thread_entry_t* entry, mycss_declaration_entry_t* dec_entry,
346                                             modest_style_raw_specificity_t* raw_spec)
347 {
348     if(entry->declaration == NULL) {
349         entry->declaration = entry->declaration_last = mcobject_async_malloc(found_context->finder_thread->declaration_obj,
350                                                                              found_context->context->declaration_node_id, NULL);
351 
352         entry->declaration->entry     = dec_entry;
353         entry->declaration->raw_spec  = *raw_spec;
354         entry->declaration->next      = NULL;
355         entry->declaration->prev      = NULL;
356 
357         return;
358     }
359 
360     modest_finder_thread_declaration_t* thr_dec = entry->declaration;
361 
362     while(thr_dec) {
363         if(thr_dec->entry->type == dec_entry->type)
364         {
365             if(modest_finder_thread_spec_is_up(raw_spec, &thr_dec->raw_spec)) {
366                 thr_dec->entry    = dec_entry;
367                 thr_dec->raw_spec = *raw_spec;
368             }
369 
370             return;
371         }
372 
373         thr_dec = thr_dec->next;
374     }
375 
376     thr_dec = mcobject_async_malloc(found_context->finder_thread->declaration_obj,
377                                     found_context->context->declaration_node_id, NULL);
378 
379     thr_dec->next     = NULL;
380     thr_dec->entry    = dec_entry;
381     thr_dec->raw_spec = *raw_spec;
382 
383     entry->declaration_last->next = thr_dec;
384     thr_dec->prev = entry->declaration_last;
385 
386     entry->declaration_last = thr_dec;
387 }
388 
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)389 void modest_finder_thread_declaratin_list_replace(modest_finder_thread_found_context_t* found_context,
390                                                   modest_finder_thread_entry_t* entry, mycss_declaration_entry_t* dec_entry,
391                                                   mycss_selectors_specificity_t* spec)
392 {
393     while(dec_entry) {
394         modest_style_raw_specificity_t raw_spec = {((unsigned int)dec_entry->is_important), spec->a, spec->b, spec->c};
395 
396         modest_finder_thread_declaratin_append(found_context, false, entry, dec_entry, &raw_spec);
397 
398         dec_entry = dec_entry->next;
399     }
400 }
401 
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)402 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)
403 {
404     modest_finder_thread_found_context_t* found_context = (modest_finder_thread_found_context_t*)ctx;
405     modest_finder_thread_context_t* thread_context = found_context->context;
406 
407     if(thread_context->entry_last) {
408         modest_finder_thread_entry_t* entry = thread_context->entry;
409 
410         while(entry)
411         {
412             if(entry->node == node) {
413                 modest_finder_thread_declaratin_list_replace(found_context, entry, selector_list->declaration_entry, spec);
414                 return;
415             }
416 
417             entry = entry->next;
418         }
419     }
420 
421     modest_finder_thread_entry_t* entry = mcobject_async_malloc(found_context->finder_thread->entry_obj, thread_context->entry_node_id, NULL);
422     memset(entry, 0, sizeof(modest_finder_thread_entry_t));
423 
424     entry->node = node;
425 
426     modest_finder_thread_declaratin_list_replace(found_context, entry, selector_list->declaration_entry, spec);
427 
428     if(thread_context->entry_last) {
429         entry->prev = thread_context->entry_last;
430 
431         thread_context->entry_last->next = entry;
432         thread_context->entry_last = entry;
433     }
434     else {
435         thread_context->entry_last = thread_context->entry = entry;
436     }
437 }
438 
modest_finder_thread_stream_single(modest_finder_thread_t * finder_thread,mycss_selectors_list_t * selector_list)439 void modest_finder_thread_stream_single(modest_finder_thread_t* finder_thread, mycss_selectors_list_t* selector_list)
440 {
441     modest_finder_thread_found_context_t found_ctx = {finder_thread, finder_thread->context_list};
442 
443     while(selector_list)
444     {
445         for(size_t i = 0; i < selector_list->entries_list_length; i++) {
446             mycss_selectors_entries_list_t *entries = &selector_list->entries_list[i];
447             mycss_selectors_specificity_t spec = entries->specificity;
448 
449             modest_finder_node_combinator_begin(finder_thread->finder, finder_thread->base_node, selector_list,
450                                                 entries->entry, &spec, modest_finder_thread_callback_found, &found_ctx);
451         }
452 
453         selector_list = selector_list->next;
454     }
455 }
456 
457 #ifndef MyCORE_BUILD_WITHOUT_THREADS
modest_finder_thread_stream(mythread_id_t thread_id,void * arg)458 void modest_finder_thread_stream(mythread_id_t thread_id, void* arg)
459 {
460     mythread_context_t* ctx = (mythread_context_t*)arg;
461     modest_finder_thread_t* finder_thread = (modest_finder_thread_t*)ctx->mythread->context;
462     mycss_selectors_list_t* selector_list = finder_thread->selector_list;
463 
464     modest_finder_thread_found_context_t found_ctx = {finder_thread, &finder_thread->context_list[ctx->id]};
465 
466     size_t count = 0, counnt_done = ctx->id - 1;
467     size_t offset = (ctx->mythread->entries_size - 1);
468 
469     while(selector_list) {
470         for(size_t i = 0; i < selector_list->entries_list_length; i++) {
471             /* split selectors by thread id */
472             if(count == counnt_done) {
473                 mycss_selectors_entries_list_t *entries = &selector_list->entries_list[i];
474                 mycss_selectors_specificity_t spec = entries->specificity;
475 
476                 modest_finder_node_combinator_begin(finder_thread->finder, finder_thread->base_node, selector_list,
477                                                     entries->entry, &spec, modest_finder_thread_callback_found, &found_ctx);
478 
479                 counnt_done += offset;
480             }
481 
482             count++;
483         }
484 
485         selector_list = selector_list->next;
486     }
487 }
488 #endif
489 
490