test-define-race.c 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169
  1. /* Copyright (C) 2009 Free Software Foundation, Inc.
  2. *
  3. * This library is free software; you can redistribute it and/or
  4. * modify it under the terms of the GNU Lesser General Public
  5. * License as published by the Free Software Foundation; either
  6. * version 2.1 of the License, or (at your option) any later version.
  7. *
  8. * This library is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. * Lesser General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Lesser General Public
  14. * License along with this library; if not, write to the Free Software
  15. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  16. */
  17. /*
  18. * test-define-race.c
  19. *
  20. * Program to exhibit a race in the guile-1.8.x define code.
  21. * See https://savannah.gnu.org/bugs/index.php?24867 for general
  22. * status and description.
  23. *
  24. * Summary: variable definition and lookup is not thread-safe in guile;
  25. * attempting to look up a variable while another thread is defining
  26. * a variable can sometimes lead to the first thread loosing, and not
  27. * seeing an existing, defined variable. Alternately, difining two
  28. * different variables at the same time can result in one of them
  29. * failing to be defined; on rarer occasions, a seg-fault results.
  30. *
  31. * Compile as:
  32. * cc test-define-race.c -lpthread -lguile
  33. *
  34. * May need to run several times to see the bug(s).
  35. *
  36. * Linas Vepstas <linasvepstas@gmail.com> December 2008
  37. */
  38. #include <libguile.h>
  39. #include <pthread.h>
  40. #include <stdio.h>
  41. #include <stdlib.h>
  42. typedef struct
  43. {
  44. int id;
  45. int count;
  46. int do_exit;
  47. int error_count;
  48. int last_ok;
  49. } state;
  50. static void * guile_mode_definer(void * ud)
  51. {
  52. SCM result;
  53. int val;
  54. char buff[1000];
  55. state * s = (state *) ud;
  56. /* Increment before evaluation, in case evaluation raises an
  57. error. */
  58. s->count ++;
  59. if (s->last_ok)
  60. {
  61. /* See if the previous definition holds the expected value. If
  62. the expected value is undefined, scm_c_eval_string will raise
  63. an error. */
  64. s->error_count ++;
  65. s->last_ok = 0;
  66. sprintf (buff, "x%d-%d\n", s->id, s->count - 1);
  67. result = scm_c_eval_string (buff);
  68. val = scm_to_int(result);
  69. if (val != s->count - 1)
  70. printf ("Define mismatch on thread %d\n", s->id);
  71. else
  72. s->error_count --;
  73. }
  74. /* Define a new variable with a new value. */
  75. sprintf (buff, "(define x%d-%d %d)\n", s->id, s->count, s->count);
  76. scm_c_eval_string (buff);
  77. /* If we reach here, the definition was apparently successful, so we
  78. can check it on the next iteration. */
  79. s->last_ok = 1;
  80. return NULL;
  81. }
  82. static void * definer (void *ud)
  83. {
  84. int i;
  85. state * s = (state *) ud;
  86. while(!s->do_exit)
  87. for (i=0; i<4000; i++)
  88. {
  89. scm_with_guile (guile_mode_definer, ud);
  90. sched_yield(); /* try to get the threads to inter-leave a lot */
  91. }
  92. return NULL;
  93. }
  94. static void init_ctr(state *s, int val)
  95. {
  96. s->id = val;
  97. s->count = 0;
  98. s->do_exit = 0;
  99. s->error_count = 0;
  100. s->last_ok = 0;
  101. }
  102. static void * setup(void * ud)
  103. {
  104. int *duration = (int *)ud;
  105. /* Query an environment variable to find out how long to run this
  106. test for, defaulting to 10s. */
  107. *duration = scm_to_int (scm_c_eval_string ("(catch #t "
  108. "(lambda () "
  109. " (round (string->number (string-append \"#e\" "
  110. "(or (getenv \"GUILE_TEST_DEFINE_RACE_DURATION\") \"10\"))))) "
  111. "(lambda _ "
  112. " (write _) (newline) 10))"));
  113. return NULL;
  114. }
  115. int main(int argc, char ** argv)
  116. {
  117. pthread_t th1, th2, th3, th4;
  118. state counter1, counter2, counter3, counter4;
  119. int error_total;
  120. int duration;
  121. scm_with_guile (setup, &duration);
  122. init_ctr (&counter1, 1);
  123. init_ctr (&counter2, 2);
  124. init_ctr (&counter3, 3);
  125. init_ctr (&counter4, 4);
  126. pthread_create(&th1, NULL, definer, (void *) &counter1);
  127. pthread_create(&th2, NULL, definer, (void *) &counter2);
  128. pthread_create(&th3, NULL, definer, (void *) &counter3);
  129. pthread_create(&th4, NULL, definer, (void *) &counter4);
  130. sleep(duration);
  131. counter1.do_exit = 1;
  132. counter2.do_exit = 1;
  133. counter3.do_exit = 1;
  134. counter4.do_exit = 1;
  135. pthread_join(th1, NULL);
  136. pthread_join(th2, NULL);
  137. pthread_join(th3, NULL);
  138. pthread_join(th4, NULL);
  139. error_total = (counter1.error_count + counter2.error_count +
  140. counter3.error_count + counter4.error_count);
  141. printf("test-define-race: %d error(s) in %ds\n", error_total, duration);
  142. exit (error_total);
  143. }