GOGOGOGOGOGOG
    • Create new note
    • Create a note from template
      • Sharing URL Link copied
      • /edit
      • View mode
        • Edit mode
        • View mode
        • Book mode
        • Slide mode
        Edit mode View mode Book mode Slide mode
      • Customize slides
      • Note Permission
      • Read
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Write
        • Only me
        • Signed-in users
        • Everyone
        Only me Signed-in users Everyone
      • Engagement control Commenting, Suggest edit, Emoji Reply
      • Invitee
      • No invitee
    • Publish Note

      Publish Note

      Everyone on the web can find and read all notes of this public team.
      Once published, notes can be searched and viewed by anyone online.
      See published notes
      Please check the box to agree to the Community Guidelines.
    • Commenting
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
      • Everyone
    • Suggest edit
      Permission
      Disabled Forbidden Owners Signed-in users Everyone
    • Enable
    • Permission
      • Forbidden
      • Owners
      • Signed-in users
    • Emoji Reply
    • Enable
    • Versions and GitHub Sync
    • Note settings
    • Engagement control
    • Transfer ownership
    • Delete this note
    • Save as template
    • Insert from template
    • Import from
      • Dropbox
      • Google Drive
      • Gist
      • Clipboard
    • Export to
      • Dropbox
      • Google Drive
      • Gist
    • Download
      • Markdown
      • HTML
      • Raw HTML
Menu Note settings Sharing URL Create Help
Create Create new note Create a note from template
Menu
Options
Versions and GitHub Sync Engagement control Transfer ownership Delete this note
Import from
Dropbox Google Drive Gist Clipboard
Export to
Dropbox Google Drive Gist
Download
Markdown HTML Raw HTML
Back
Sharing URL Link copied
/edit
View mode
  • Edit mode
  • View mode
  • Book mode
  • Slide mode
Edit mode View mode Book mode Slide mode
Customize slides
Note Permission
Read
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Write
Only me
  • Only me
  • Signed-in users
  • Everyone
Only me Signed-in users Everyone
Engagement control Commenting, Suggest edit, Emoji Reply
Invitee
No invitee
Publish Note

Publish Note

Everyone on the web can find and read all notes of this public team.
Once published, notes can be searched and viewed by anyone online.
See published notes
Please check the box to agree to the Community Guidelines.
Engagement control
Commenting
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
  • Everyone
Suggest edit
Permission
Disabled Forbidden Owners Signed-in users Everyone
Enable
Permission
  • Forbidden
  • Owners
  • Signed-in users
Emoji Reply
Enable
Import from Dropbox Google Drive Gist Clipboard
   owned this note    owned this note      
Published Linked with GitHub
1
Subscribed
  • Any changes
    Be notified of any changes
  • Mention me
    Be notified of mention me
  • Unsubscribe
Subscribe
# POSIX Thread-safe Linked List ## POSIX 執行緒 首先先了解在`ll.h `裡的`pthread.h `內容和搞清楚什麼是thread-safe linked list:**POSIX執行緒(POSIX Threads,縮寫為Pthreads)是POSIX的執行緒標準,定義了創建和操縱執行緒的一套API。** 簡單來說,我們如果要將c語言的程式平行化,最基本的方式就是使用 POSIX 執行緒(簡稱 pthread)來實做多執行緒的程式。以下是一個pthread 函式庫的用法的實際範例: ```clike #include <stdio.h> #include <pthread.h> #include <unistd.h> // 執行緒函數 void* child(void* data) { char *str = (char*) data; // 取得輸入資料 for(int i = 0;i < 3;++i) { printf("%s\n", str); // 每秒輸出文字 sleep(1); } pthread_exit(NULL); // 離開子執行緒 } // 主程式 int main() { pthread_t t; // 宣告 pthread 變數 pthread_create(&t, NULL, child, "Child"); // 建立子執行緒 // 主執行緒工作 for(int i = 0;i < 3;++i) { printf("Master\n"); // 每秒輸出文字 sleep(1); } pthread_join(t, NULL); // 等待子執行緒執行完成 return 0; } ``` pthread 的 pthread_create 函數可以用來建立新的執行緒,並以函數指標指定子執行緒所要執行的函數,子執行緒在建立之後,就會以平行的方式執行,在子執行緒的執行期間,主執行緒還是可以正常執行自己的工作,最後主執行緒再以 pthread_join 函數等待子執行緒執行結束,處理後續收尾的動作。其程式執行結果如下: ```shell $ gcc test.c -lpthread -o test $ ./test Master Child Master Child Master Child ``` 使用`gcc `編譯時須加上`-lpthread `,例如:` gcc test.c -lpthread -o test ` ## 互斥鎖(Mutex) 在平行化的程式中,如果發生多個執行緒需要同時存取同一個位置的資料時,就有可能會因為同時存取而產生錯誤,在下面這個例子中,我們定義一個全域變數 counter,用來紀錄某個量的總和,而我們希望在多個執行緒中同時計算,然後統一將加總的結果放在其中。 ```clike #include <stdio.h> #include <pthread.h> #include <unistd.h> // 計數器 int counter = 0; // 執行緒函數 void* child() { for(int i = 0;i < 3;++i) { int tmp = counter; sleep(1); // 故意讓它延遲一下 counter = tmp + 1; printf("Counter = %d\n", counter); } pthread_exit(NULL); } // 主程式 int main() { pthread_t t1, t2; pthread_create(&t1, NULL, child, NULL); pthread_create(&t2, NULL, child, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); return 0; } ``` 在這段程式碼中,我們放了兩個子執行緒,每個子執行緒用迴圈跑了三次計算,所以最後的 counter 預期應該是 6,但由於我們將 counter 的值取出來,計算出新的值之後在放回去,兩個子執行緒同時都這樣做的話,計算結果就會不如預期: ```shell $ gcc test.c -lpthread -o test $ ./test Counter = 1 Counter = 1 Counter = 2 Counter = 2 Counter = 3 Counter = 3 ``` 這個問題的解決方法就是加入一個互斥鎖(mutex),將那些不可以被多個執行緒同時執行的程式碼片段,用互斥鎖包起來,當一個執行緒執行到該處時,就會先上鎖,避免其他的執行緒進入,若其他的執行緒同時也要執行該處的程式碼時,就必須等待先前的執行緒執行完之後,才能接著進入(也就是排隊輪流使用的概念),這樣就可以避免多個執行緒混雜執行,讓結果出錯的問題。 ```clike #include <stdio.h> #include <pthread.h> #include <unistd.h> // 計數器 int counter = 0; // 加入 Mutex pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER; // 子執行緒函數 void* child() { for(int i = 0;i < 3;++i) { pthread_mutex_lock( &mutex1 ); // 上鎖 int tmp = counter; sleep(1); counter = tmp + 1; pthread_mutex_unlock( &mutex1 ); // 解鎖 printf("Counter = %d\n", counter); } pthread_exit(NULL); } // 主程式 int main() { pthread_t t1, t2; pthread_create(&t1, NULL, child, NULL); pthread_create(&t2, NULL, child, NULL); pthread_join(t1, NULL); pthread_join(t2, NULL); return 0; } ``` 執行結果: ```shell $ ./test Counter = 1 Counter = 2 Counter = 3 Counter = 4 Counter = 5 Counter = 6 ``` https://gist.github.com/viking/2521704 ll.c之程式碼執行結果: ```shell $ gcc -I"include" -Wall ll.c -lpthread -o ll $ ./ll PASS Test 1! PASS Test 2! PASS Test 3! PASS Test 4! PASS Test 5! PASS Test 6! PASS Test 7! PASS Test 8! PASS Test 9! PASS Test 10! PASS Test 11! PASS Test 12! PASS Test 13! (ll: 1 5 6), length: 3 PASSED all 14 tests! ``` --- ## 以 [concurrent-ll](https://github.com/jserv/concurrent-ll) 中 main.c 為參考對 [ll](https://github.com/r-medina/ll) 做測試 <small>[GitHub](https://github.com/happyincent/ll)</small> ```shell $ cat <(echo "CPU: " `lscpu | grep "Model name" | cut -d':' -f2 | sed "s/ //"`) <(echo "OS: " `lsb_release -d | cut -f2`) <(echo "Kernel: " `uname -a | cut -d' ' -f1,3,14`) <(echo "gcc: " `gcc --version | head -n1`) CPU: Intel(R) Xeon(R) CPU E5520 @ 2.27GHz OS: Ubuntu 16.04.5 LTS Kernel: Linux 4.15.0-43-generic x86_64 gcc: gcc (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609 ``` ### Illegal instruction (core dumped) ``` $ gdb -q --args ./bin/test "-n 2" Reading symbols from ./bin/test...done. (gdb) r Starting program: /home/itlab/Desktop/ll/bin/test -n\ 2 [Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". [New Thread 0x7ffff77ef700 (LWP 31597)] [New Thread 0x7ffff6fee700 (LWP 31598)] Thread 3 "test" received signal SIGILL, Illegal instruction. [Switching to Thread 0x7ffff6fee700 (LWP 31598)] __GI___pthread_rwlock_unlock (rwlock=rwlock@entry=0x7ffff00083c0) at pthread_rwlock_unlock.c:38 38 pthread_rwlock_unlock.c: No such file or directory. (gdb) info threads Id Target Id Frame 1 Thread 0x7ffff7fda700 (LWP 29705) "test" 0x00007ffff7bcac1d in nanosleep () at ../sysdeps/unix/syscall-template.S:84 2 Thread 0x7ffff77ef700 (LWP 29709) "test" 0x00007ffff7bc6730 in futex_wait (private=<optimized out>, expected=6, futex_word=0x7fffe8004e5c) at ../sysdeps/unix/sysv/linux/futex-internal.h:61 * 3 Thread 0x7ffff6fee700 (LWP 29710) "test" __GI___pthread_rwlock_unlock (rwlock=rwlock@entry=0x7ffff0005710) at pthread_rwlock_unlock.c:38 (gdb) backtrace #0 __GI___pthread_rwlock_unlock (rwlock=rwlock@entry=0x7ffff0005710) at pthread_rwlock_unlock.c:38 #1 0x0000000000401272 in ll_select_n_min_1 (list=list@entry=0x604010, node=node@entry=0x7ffff6fedee0, n=967, n@entry=977, lt=lt@entry=l_write) at src/ll.c:177 #2 0x0000000000401301 in ll_insert_n (list=0x604010, val=val@entry=0x7ffff6fedf20, n=977) at src/ll.c:204 #3 0x0000000000401382 in ll_insert_last (list=<optimized out>, val=val@entry=0x7ffff6fedf20) at src/ll.c:245 #4 0x0000000000401963 in test (data=0x6040a8) at src/test.c:156 #5 0x00007ffff7bc16ba in start_thread (arg=0x7ffff6fee700) at pthread_create.c:333 #6 0x00007ffff78f741d in clone () at ../sysdeps/unix/sysv/linux/x86_64/clone.S:109 ``` 註解掉 `test.c` 中的 `ll_remove_search` ``` $ ./bin/test -n 2 Thread 0 #operations : 32673356 #inserts : 1 #removes : 0 Thread 1 #operations : 5396980 #inserts : 1 #removes : 0 Duration : 1001 (ms) #txs : 38070336 (38032303.696304 / s) Expected size: 1025 Actual size: 1025 ``` 保留 `test.c` 中的 `ll_remove_search`, 註解掉 `ll_remove_search` 中的 `free(node);` ``` $ ./bin/test -n 2 Thread 0 #operations : 172293 #inserts : 17540 #removes : 17539 Thread 1 #operations : 171544 #inserts : 17481 #removes : 17480 Duration : 1000 (ms) #txs : 343837 (343837.000000 / s) Expected size: 1025 Actual size: 1025 ``` 由上述結果得知 `ll_remove_search` 在 ==刪除、free node== 時沒有 LOCK 同時又有其他 thread 存取該 node 就會出錯 參考 [concurrent-ll/lock/list.c](https://github.com/jserv/concurrent-ll/blob/master/src/lock/list.c) 對 `ll.c` 中的 `ll_remove_search()` 及新加入的 `ll_search()` 進行修改如下: ```c int ll_remove_search(ll_t *list, int cond(void *, void *), void *val) { if (list == NULL) return -1; RWLOCK(l_write, list->m); if (list->hd == NULL) { RWUNLOCK(list->m); return -1; } ll_node_t *tmp = NULL; ll_node_t *prev = NULL; ll_node_t *node = list->hd; RWLOCK(l_write, node->m); if (cond(node->val, val)) { // list->hd->val == val tmp = node->nxt; if (tmp != NULL) RWLOCK(l_write, tmp->m); list->hd = tmp; (list->len)--; if (tmp != NULL) RWUNLOCK(tmp->m); RWUNLOCK(list->m); list->val_teardown(node->val); RWUNLOCK(node->m); pthread_rwlock_destroy(&(node->m)); free(node); return list->len; } prev = node; node = node->nxt; if (node != NULL) RWLOCK(l_write, node->m); RWUNLOCK(list->m); while (node != NULL) { if (cond(node->val, val)) { tmp = node->nxt; if (tmp != NULL) RWLOCK(l_write, tmp->m); prev->nxt = tmp; if (tmp != NULL) RWUNLOCK(tmp->m); RWUNLOCK(prev->m); list->val_teardown(node->val); RWUNLOCK(node->m); pthread_rwlock_destroy(&(node->m)); free(node); RWLOCK(l_write, list->m); (list->len)--; RWUNLOCK(list->m); return list->len; } RWUNLOCK(prev->m); prev = node; node = node->nxt; if (node != NULL) RWLOCK(l_write, node->m); } RWUNLOCK(prev->m); return -1; } ``` 註解掉 test.c 中的 ll_search ```c /* Wait on barrier */ barrier_cross(d->barrier); // start the test while (*running) { // generate a value (node that rand_max is expected to be a power of 2) the_value = my_random(&seeds[0], &seeds[1], &seeds[2]) & rand_max; // generate the operation op = my_random(&seeds[0], &seeds[1], &seeds[2]) & 0xff; if (op < read_thresh) { // do a find operation // ll_search(the_list, num_equals, &the_value); } else if (last == -1) { // do a write operation if (ll_insert_last(the_list, &the_value) != -1) { d->num_insert++; last = 1; } } else { // do a delete operation if (ll_remove_search(the_list, num_equals, &the_value) != -1) { d->num_remove++; last = -1; } } d->num_operations++; } ``` 增加 thread 數量會發生 ==Segmentation fault== ``` itlab@ITLabHP:~/Desktop/ll$ ./bin/test -n 2 Thread 0 #operations : 94120 #inserts : 9193 #removes : 9193 Thread 1 #operations : 95547 #inserts : 9589 #removes : 9588 Duration : 1000 (ms) #txs : 189667 (189667.000000 / s) Expected size: 1024 Actual size: 1024 itlab@ITLabHP:~/Desktop/ll$ ./bin/test -n 128 ... Thread 127 #operations : 2362 #inserts : 226 #removes : 226 Duration : 1000 (ms) #txs : 288003 (288003.000000 / s) Expected size: 1099 Actual size: 1099 itlab@ITLabHP:~/Desktop/ll$ ./bin/test -n 256 Segmentation fault (core dumped) ``` GDB 結果 ``` itlab@ITLabHP:~/Desktop/ll$ gdb -q --args ./bin/test "-n 256" Reading symbols from ./bin/test...done. (gdb) r Starting program: /home/itlab/Desktop/ll/bin/test -n\ 256 ... Thread 236 "test" received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7ffd7c7a0700 (LWP 8921)] 0x0000000000401792 in num_equals (n=0x7ffff77eef20, m=0x7ffd7c79ff20) at src/test.c:103 103 return *(unsigned long *)n == *(unsigned long *)m; (gdb) p *(unsigned long *)n Cannot access memory at address 0x7ffff77eef20 (gdb) up 1 #1 0x00000000004014b4 in ll_remove_search (list=0x604010, cond=cond@entry=0x40178f <num_equals>, val=val@entry=0x7ffd7c79ff20) at src/ll.c:321 321 if (node->val != NULL && cond(node->val, val)) { (gdb) info threads Id Target Id Frame 1 Thread 0x7ffff7fda700 (LWP 8683) "test" 0x00007ffff78f1747 in munmap () at ../sysdeps/unix/syscall-template.S:84 * 236 Thread 0x7ffd7c7a0700 (LWP 8921) "test" 0x0000000000401792 in num_equals (n=0x7ffff77eef20, m=0x7ffd7c79ff20) at src/test.c:103 ``` 將所有 `struct ll_node` 中的 val 從 `void *` 改為 `int` ```c int num_equals(int n, int m) { return n == m; } ``` 執行結果 ```c // start the test while (*running) { // generate a value (node that rand_max is expected to be a power of 2) the_value = my_random(&seeds[0], &seeds[1], &seeds[2]) & rand_max; // generate the operation op = my_random(&seeds[0], &seeds[1], &seeds[2]) & 0xff; if (op < read_thresh) { // do a find operation ll_search(the_list, num_equals, the_value); } else if (last == -1) { // do a write operation ll_insert_last(the_list, the_value); d->num_insert++; last = 1; } else { // do a delete operation if (ll_remove_search(the_list, num_equals, the_value) != -1) { d->num_remove++; last = -1; } } d->num_operations++; } ``` ``` itlab@ITLabHP:~/Desktop/ll$ ./bin/test -n 1024 | tail -n7 Thread 1023 #operations : 4 #inserts : 1 #removes : 0 Duration : 1000 (ms) #txs : 5579 (5579.000000 / s) Expected size: 1628 Actual size: 1628 ``` --- ### 發現問題 用valgrind分析: ```shell $ valgrind --track-origins=yes --leak-check=full ./test -n300 ... ==3623== Thread 63: ==3623== Conditional jump or move depends on uninitialised value(s) ==3623== at 0x109575: ll_remove_search (ll.c:328) ==3623== by 0x109B9D: test (test.c:162) ==3623== by 0x4E436DA: start_thread (pthread_create.c:463) ==3623== by 0x517C88E: clone (clone.S:95) ==3623== Uninitialised value was created by a stack allocation ==3623== at 0x4E438B9: advise_stack_range (allocatestack.c:386) ==3623== by 0x4E438B9: start_thread (pthread_create.c:552) ... ``` 另一個輸出: ```shell $ valgrind --track-origins=yes --leak-check=full ./test -n300 ... ==2354== Thread 288: ==2354== Invalid read of size 8 ==2354== at 0x109862: num_equals (ll.c:499) ==2354== by 0x109572: ll_remove_search (ll.c:328) ==2354== by 0x109B9D: test (test.c:162) ==2354== by 0x4E436DA: start_thread (pthread_create.c:463) ==2354== by 0x517C88E: clone (clone.S:95) ==2354== Address 0x604beb0 is not stack'd, malloc'd or (recently) free'd ... ``` Valgrind 好用的地方在於可以一步步看出是哪邊出了問題,於是我發現問題出在 ll_remove_search 裡面: ```clike int ll_remove_search(ll_t *list, int cond(void *, void *), void *val) ... while (node->nxt != NULL) { if (node->val != NULL) { if(cond(node->val, val)){ last->nxt = node->nxt; RWUNLOCK(node->m); free(node); RWUNLOCK(last->m); //RWLOCK(l_write, list->m); (list->len)--; RWUNLOCK(list->m); return list->len; } } ... ``` 因為 Valgrind 告訴我錯誤出在 if 那邊,但我一開始不知道是前面錯還後面錯所以分開寫,後來再偵錯一次發現是下面那的 if 才是造成 ==Conditional jump or move depends on uninitialised value(s)== 的原因那既然問題不是出在 node->val 那就是 val 了,於是又要追朔到最開始呼叫 ll_remove_search 的 test function 了。 ```clike void *test(void *data){ ... unsigned long the_value; ... while (*running) { // generate a value (node that rand_max is expected to be a power of 2) the_value = my_random(&seeds[0], &seeds[1], &seeds[2]) & rand_max; // generate the operation op = my_random(&seeds[0], &seeds[1], &seeds[2]) & 0xff; if (op < read_thresh) { // do a find operation // ll_search(the_list, num_equals, &the_value); } else if (last == -1) { // do a write operation if (ll_insert_last(the_list, &the_value) != -1) { d->num_insert++; last = 1; } } else { // do a delete operation if (ll_remove_search(the_list, num_equals, &the_value) != -1) { d->num_remove++; last = -1; } } d->num_operations++; } ... } ``` 於是我懷疑是第 3 行的 the_value 的 address 傳入 ll_remove_search 後裡面的值被釋放掉了,所以為了確保他一直存在所以我改 the_value 的前面加上 static ```c static unsigned long the_value; ``` 然後就沒再出現 segmentation fault ,但為什麼 the_value 的值會被洗掉目前還不是很確定,但我用 valgrind 測試是沒有錯誤了。 ``` $ valgrind --track-origins=yes --leak-check=full ./test -n300 ... ==24167== Command: ./test -n300 ==24167== Expected size: 1321 Actual size: 1321 ==24167== ==24167== LEAK SUMMARY: ==24167== definitely lost: 0 bytes in 0 blocks ==24167== indirectly lost: 0 bytes in 0 blocks ==24167== possibly lost: 0 bytes in 0 blocks ==24167== still reachable: 95,200 bytes in 1,322 blocks ==24167== suppressed: 0 bytes in 0 blocks ==24167== Reachable blocks (those to which a pointer was found) are not shown. ==24167== To see them, rerun with: --leak-check=full --show-leak-kinds=all ==24167== ==24167== For counts of detected and suppressed errors, rerun with: -v ==24167== ERROR SUMMARY: 0 errors from 0 contexts (suppressed: 0 from 0) ... ``` 此外我也實際測試增加更多的thread也都可以順利的執行。我覺得會 debug 這麼久是因為一開始都是以為是 lock 沒有鎖好,造成 race condition 才會出錯。 ### 發現 memory leak 在參考老師的 [concurrent-ll](https://github.com/jserv/concurrent-ll) 時,發現 lock 裡面 main.c 的 test 結束時沒有 free 用 posix_memalign 要的空間。 又是 valgrind 提醒了我: ```shell ==28738== 192 bytes in 3 blocks are definitely lost in loss record 2 of 4 ==28738== at 0x4C320A6: memalign (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==28738== by 0x10994F: seed_rand (random.h:39) ==28738== by 0x10994F: test (test.c:118) ==28738== by 0x4E436DA: start_thread (pthread_create.c:463) ==28738== by 0x517C88E: clone (clone.S:95) ==28738== ==28738== LEAK SUMMARY: ==28738== definitely lost: 192 bytes in 3 blocks ==28738== indirectly lost: 0 bytes in 0 blocks ==28738== possibly lost: 0 bytes in 0 blocks ==28738== still reachable: 73,960 bytes in 1,027 blocks ==28738== suppressed: 0 bytes in 0 blocks ``` * random.h 裡面 ```clike void *memalign(size_t size, size_t alignment) { void *buffer; posix_memalign(&buffer, alignment, size); return buffer; } unsigned long *seed_rand() { unsigned long *seeds; seeds = (unsigned long *)memalign(64, 64); // allocate 64 byte and return a pointer to the allocated memory seeds[0] = getticks() % 123456789; // which is mutiple of 64 seeds[1] = getticks() % 362436069; seeds[2] = getticks() % 521288629; return seeds; } ``` * main.c 裡面 在 test 結束時加上 free(seeds) 即可 ```clike void *test(void *data){ ... seeds = seed_rand(); ... free(seeds); return NULL; ... } ``` --- #### 測試ll_inserts_last和ll_search還有ll_insert_n功能: 將test.c中的`void *test(void *data) ` 改成如下: ```clike while (*running) { // generate a value (node that rand_max is expected to be a power of 2) the_value = my_random(&seeds[0], &seeds[1], &seeds[2]) & rand_max; // generate the operation op = my_random(&seeds[0], &seeds[1], &seeds[2]) & 0xff; if (op < read_thresh) { // do a find operation ll_search(the_list, num_equals, &the_value); d->num_search++; } else if (last == -1) { // do a write operation if (ll_insert_last(the_list, &the_value) != -1) { d->num_insert++; last = 1; } } else { // do a delete operation //if (ll_remove_search(the_list, num_equals, &the_value) != -1) { // d->num_remove++; // last = -1; // } if (ll_insert_n( the_list,&the_value,100 )!=-1){ d->num_insert_n++; last = -1; } } ``` 執行結果: ```shell $ bin/test -n 300 ... Thread 295 #operations : 113 #inserts : 15 #search : 84 #insert_n : 14 Thread 296 #operations : 135 #inserts : 14 #search : 108 #insert_n : 13 Thread 297 #operations : 79 #inserts : 9 #search : 62 #insert_n : 8 Thread 298 #operations : 95 #inserts : 9 #search : 78 #insert_n : 8 Thread 299 #operations : 96 #inserts : 11 #search : 75 #insert_n : 10 Duration : 1000 (ms) #txs : 36453 (36453.000000 / s) Expected size: 4810 Actual size: 8381 ``` 但測試超過1024個thread便會產生`core dumped ` ```clike $ bin/test -n 1024 程式記憶體區段錯誤 (core dumped) ``` 但在gdb的環境下測試**有時候**是不會有問題的: ```shell ... Thread 1019 #operations : 14 #inserts : 2 #search : 11 #insert_n : 1 Thread 1020 #operations : 15 #inserts : 1 #search : 13 #insert_n : 1 Thread 1021 #operations : 15 #inserts : 2 #search : 12 #insert_n : 1 Thread 1022 [Thread 0x7ffdc65e4700 (LWP 4503) exited] #operations : 10 #inserts : 0 #search : 10 #insert_n : 0 Thread 1023 #operations : 10 #inserts : 1 #search : 9 #insert_n : 0 Duration : 1000 (ms) #txs : 21241 (21241.000000 / s) Expected size: 3520 Actual size: 5400 ``` 但仍會出現問題: ```shell Thread 167 "test" received signal SIGSEGV, Segmentation fault. [Switching to Thread 0x7ffea3f9f700 (LWP 6139)] 0x0000000000401792 in num_equals (n=0x7fff9a7fbf20, m=0x7ffea3f9ef20) at src/test.c:104 104 return *(unsigned long *)n == *(unsigned long *)m; (gdb) ``` 利用valgrind發現,問題是出現在 ```shell at 0x4C2DB8F: malloc (in /usr/lib/valgrind/vgpreload_memcheck-amd64-linux.so) ==8926== by 0x4011A1: ll_new_node (ll.c:132) ==8926== by 0x4012C3: ll_insert_n (ll.c:195) ==8926== by 0x401381: ll_insert_last (ll.c:245) ==8926== by 0x4019B2: test (test.c:136) ==8926== by 0x4E416B9: start_thread (pthread_create.c:333) ``` 也就是 ```clike ll_node_t *new_node = ll_new_node(val); ... return ll_insert_n(list, val, list->len); if (ll_insert_last(the_list, (int *)the_value) == -1) ``` 我覺得問題應該是出在val到底該用什麼型態才好,我嘗試將`val `改為`int * val `並且將test.c中的將每個函是的回傳值改為`ll_insert_last(the_list, (int *)the_value) `但還是出現了core dumped,看樣子`val `也不能是`int * `再試著改改看! ### 測試1萬個thread ```shell $ bin/test -n10000 Thread 9999 #operations : 4 #inserts : 0 #search : 4 #insert_n : 0 Duration : 1018 (ms) #txs : 80800 (79371.316306 / s) Expected size: 12502 Actual size: 17543 ```

Import from clipboard

Advanced permission required

Your current role can only read. Ask the system administrator to acquire write and comment permission.

This team is disabled

Sorry, this team is disabled. You can't edit this note.

This note is locked

Sorry, only owner can edit this note.

Reach the limit

Sorry, you've reached the max length this note can be.
Please reduce the content or divide it to more notes, thank you!

Import from Gist

Import from Snippet

or

Export to Snippet

Are you sure?

Do you really want to delete this note?
All users will lose their connection.

Create a note from template

Create a note from template

Oops...
This template is not available.
Upgrade
All
  • All
  • Team
No template found.

Create custom template

Upgrade

Delete template

Do you really want to delete this template?
Turn this template into a regular note and keep its content, versions, and comments.

This page need refresh

You have an incompatible client version.
Refresh to update.
New version available!
See releases notes here
Refresh to enjoy new features.
Your user state has changed.
Refresh to load new user state.

Sign in

Forgot password

or

By clicking below, you agree to our terms of service.

Sign in via Facebook Sign in via Twitter Sign in via GitHub Sign in via Dropbox Sign in with Wallet
Wallet ( )
Connect another wallet

New to HackMD? Sign up

Help

  • English
  • 中文
  • Français
  • Deutsch
  • 日本語
  • Español
  • Català
  • Ελληνικά
  • Português
  • italiano
  • Türkçe
  • Русский
  • Nederlands
  • hrvatski jezik
  • język polski
  • Українська
  • हिन्दी
  • svenska
  • Esperanto
  • dansk

Documents

Help & Tutorial

How to use Book mode

How to use Slide mode

API Docs

Edit in VSCode

Install browser extension

Get in Touch

Feedback

Discord

Send us email

Resources

Releases

Pricing

Blog

Policy

Terms

Privacy

Cheatsheet

Syntax Example Reference
# Header Header 基本排版
- Unordered List
  • Unordered List
1. Ordered List
  1. Ordered List
- [ ] Todo List
  • Todo List
> Blockquote
Blockquote
**Bold font** Bold font
*Italics font* Italics font
~~Strikethrough~~ Strikethrough
19^th^ 19th
H~2~O H2O
++Inserted text++ Inserted text
==Marked text== Marked text
[link text](https:// "title") Link
![image alt](https:// "title") Image
`Code` Code 在筆記中貼入程式碼
```javascript
var i = 0;
```
var i = 0;
:smile: :smile: Emoji list
{%youtube youtube_id %} Externals
$L^aT_eX$ LaTeX
:::info
This is a alert area.
:::

This is a alert area.

Versions and GitHub Sync
Upgrade to Prime Plan

  • Edit version name
  • Delete

revision author avatar     named on  

More Less

No updates to save
Compare
    Choose a version
    No search result
    Version not found
Sign in to link this note to GitHub
Learn more
This note is not linked with GitHub
 

Feedback

Submission failed, please try again

Thanks for your support.

On a scale of 0-10, how likely is it that you would recommend HackMD to your friends, family or business associates?

Please give us some advice and help us improve HackMD.

 

Thanks for your feedback

Remove version name

Do you want to remove this version name and description?

Transfer ownership

Transfer to
    Warning: is a public team. If you transfer note to this team, everyone on the web can find and read this note.

      Link with GitHub

      Please authorize HackMD on GitHub
      • Please sign in to GitHub and install the HackMD app on your GitHub repo.
      • HackMD links with GitHub through a GitHub App. You can choose which repo to install our App.
      Learn more  Sign in to GitHub

      Push the note to GitHub Push to GitHub Pull a file from GitHub

        Authorize again
       

      Choose which file to push to

      Select repo
      Refresh Authorize more repos
      Select branch
      Select file
      Select branch
      Choose version(s) to push
      • Save a new version and push
      • Choose from existing versions
      Include title and tags
      Available push count

      Upgrade

      Pull from GitHub

       
      File from GitHub
      File from HackMD

      GitHub Link Settings

      File linked

      Linked by
      File path
      Last synced branch
      Available push count

      Upgrade

      Danger Zone

      Unlink
      You will no longer receive notification when GitHub file changes after unlink.

      Syncing

      Push failed

      Push successfully