CUBRID Engine  latest
wait_for_graph.c
Go to the documentation of this file.
1 /*
2  * Copyright 2008 Search Solution Corporation
3  * Copyright 2016 CUBRID Corporation
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at
8  *
9  * http://www.apache.org/licenses/LICENSE-2.0
10  *
11  * Unless required by applicable law or agreed to in writing, software
12  * distributed under the License is distributed on an "AS IS" BASIS,
13  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14  * See the License for the specific language governing permissions and
15  * limitations under the License.
16  *
17  */
18 
19 /*
20  * wait_for_graph.c - management of Wait-For-Graph
21  */
22 
23 #ident "$Id$"
24 
25 #if defined(ENABLE_UNUSED_FUNCTION)
26 #include "config.h"
27 
28 #include <stddef.h>
29 #include <assert.h>
30 
31 #include "error_manager.h"
32 #include "memory_alloc.h"
33 #include "wait_for_graph.h"
34 #include "critical_section.h"
35 #if defined(SERVER_MODE)
36 #include "connection_error.h"
37 #endif /* SERVER_MODE */
38 
39 /* Prune the number of found cycles in a cycle group */
40 static const int WFG_PRUNE_CYCLES_IN_CYCLE_GROUP = 10;
41 static const int WFG_MAX_CYCLES_TO_REPORT = 100;
42 
43 /* status of a node in WFG */
44 typedef enum
45 {
46  WFG_NOT_VISITED,
47  WFG_ON_STACK,
48  WFG_OFF_STACK,
49  WFG_RE_ON_STACK,
50  WFG_ON_TG_CYCLE
51 } WFG_STACK_STATUS;
52 
53 /* structure of an edge in WFG */
54 typedef struct wfg_edge WFG_EDGE;
55 struct wfg_edge
56 {
57  int waiter_tran_index; /* index of waiter transaction */
58  int holder_tran_index; /* index of holder transaction */
59  struct wfg_edge *next_holder_edge_p; /* pointer to next holder */
60  struct wfg_edge *next_waiter_edge_p; /* pointer to next waiter */
61 };
62 
63 /* structure of a node in WFG */
64 typedef struct wfg_node WFG_NODE;
65 struct wfg_node
66 {
67  WFG_STACK_STATUS status;
68  int cycle_group_no; /* Group no in a cycle */
69  /*
70  * Fun to call to solve a cycle. If NULL, the transaction will be aborted.
71  * Assumption a transaction can be waiting for many transaction,
72  * but it can only be waiting in one place.
73  */
74  int (*cycle_fun) (int tran_index, void *args);
75  void *args; /* Arguments to be passed to cycle_fun */
76  WFG_EDGE *first_holder_edge_p; /* pointer to first holder */
77  WFG_EDGE *last_holder_edge_p; /* pointer to last holder */
78  WFG_EDGE *first_waiter_edge_p; /* pointer to first waiter */
79  WFG_EDGE *last_waiter_edge_p; /* pointer to last waiter */
80 };
81 
82 /* WFG stack to implement non-recursive DFS */
83 typedef struct wfg_stack WFG_STACK;
84 struct wfg_stack
85 {
86  int wait_tran_index; /* current wait node */
87  WFG_EDGE *current_holder_edge_p; /* current holder edge */
88 };
89 
90 /* structure to maintain transaction index list for TG table */
91 typedef struct wfg_trans_list WFG_TRANS_LIST;
92 struct wfg_trans_list
93 {
94  int tran_index; /* transaction index */
95  struct wfg_trans_list *next; /* next transaction */
96 };
97 
98 /* structure of transaction group table entry */
99 typedef struct wfg_tran_group WFG_TRAN_GROUP;
100 struct wfg_tran_group
101 {
102  int num_holders;
103  int num_waiters;
104  WFG_TRANS_LIST *holder_tran_list_p; /* transaction group */
105  WFG_TRANS_LIST *waiter_tran_list_p; /* trans waiting for the TG */
106 };
107 
108 static WFG_NODE *wfg_Nodes = NULL; /* ptr to WFG nodes */
109 static int wfg_Total_nodes = 0; /* # of nodes */
110 static int wfg_Total_edges = 0; /* # of edges */
111 static int wfg_Total_waiters = 0; /* # of waiters */
112 
113 /* Transaction group table */
114 static WFG_TRAN_GROUP *wfg_Tran_group = NULL;
115 static int wfg_Total_tran_groups = 0;
116 
117 static int wfg_initialize_node (WFG_NODE * node_p);
118 static int wfg_free_group_list (void);
119 
120 static int wfg_push_stack (WFG_STACK ** top_p, int node);
121 static int wfg_pop_stack (WFG_STACK ** top_p, WFG_STACK ** bottom_p);
122 static int wfg_internal_detect_cycle (THREAD_ENTRY * thread_p, WFG_CYCLE_CASE * cycle_case_p,
123  WFG_CYCLE ** list_cycles_p, const int max_cycles_in_cycle_group,
124  const int max_cycles);
125 static int wfg_detect_ordinary_cycle (THREAD_ENTRY * thread_p, WFG_CYCLE_CASE * cycle_case_p,
126  WFG_CYCLE ** list_cycles_p, const int max_cycles_in_group, const int max_cycles);
127 static int wfg_add_waiters_of_tg (int *smallest_onstack, int holder_node, int tg_index);
128 static int wfg_add_waiters_normal_wfg (int *smallest_onstack, int node_index);
129 static int wfg_get_all_waiting_and_add_waiter (bool * all_waiting, bool * add_waiter, int tg_index);
130 static WFG_CYCLE *wfg_detect_tran_group_cycle_internal (WFG_CYCLE_CASE * cycle_case_p, WFG_TRANS_LIST * w_tran_list_p,
131  bool add_waiter, int tg_index, int num_tran_groups_holders);
132 static int wfg_detect_tran_group_cycle (THREAD_ENTRY * thread_p, WFG_CYCLE_CASE * cycle_case_p,
133  WFG_CYCLE ** list_cycles);
134 
135 static int wfg_dump_given_cycles (FILE * out_fp, WFG_CYCLE * list_cycles_p);
136 static void wfg_dump_holder_waiter (FILE * out_fp, int node_index);
137 static void wfg_dump_holder_waiter_of_tran_group (FILE * out_fp, int group_index);
138 
139 #if defined(WFG_DEBUG)
140 static int wfg_valid_tran_index (const int holder_index, const int holder_tran_index, const int waiter_tran_index);
141 static int check_duplication_holders (const int holder_index, const int *holder_tran_indices, const int num_holders,
142  const int waiter_tran_index);
143 static int wfg_check_insert_out_edges (const int waiter_tran_index, int num_holders, const int *holder_tran_indeces);
144 static int wfg_check_remove_out_edges (const int waiter_tran_index, int num_holders, const int *holder_tran_indeces);
145 #endif /* WFG_DEBUG */
146 
147 static int wfg_allocate_edges (WFG_EDGE ** first_edge_p, WFG_EDGE ** last_edge_p, const int *holder_tran_indices,
148  const int num_holders, const int waiter_tran_index);
149 static int wfg_link_edge_holders_waiter_list (WFG_EDGE * first_edge_p, WFG_EDGE * last_edge_p,
150  const int waiter_tran_index);
151 static int wfg_remove_waiter_list_of_holder_edge (WFG_NODE * node_p, WFG_EDGE ** holder_p);
152 
153 /*
154  * TODO : M2, error check
155  * wfg_push_stack : push operation on WFG stack
156  *
157  * return : NO_ERROR
158  *
159  * top_p(IN/OUT) :
160  * node(IN) :
161  */
162 static int
163 wfg_push_stack (WFG_STACK ** top_p, int node)
164 {
165  (*top_p)++;
166  (*top_p)->wait_tran_index = node;
167  (*top_p)->current_holder_edge_p = wfg_Nodes[node].first_holder_edge_p;
168 
169  return NO_ERROR;
170 }
171 
172 /*
173  * wfg_pop_stack : pop operation on WFG stack
174  *
175  * return : NO_ERROR
176  *
177  * top_p(IN/OUT) :
178  * bottom(IN) :
179  */
180 static int
181 wfg_pop_stack (WFG_STACK ** top_p, WFG_STACK ** bottom_p)
182 {
183  (*top_p)--;
184  if ((*top_p) - (*bottom_p) < 0)
185  {
186  return ER_FAILED;
187  }
188  (*top_p)->current_holder_edge_p = (*top_p)->current_holder_edge_p->next_holder_edge_p;
189 
190  return NO_ERROR;
191 }
192 
193 /*
194  * wfg_initialize_node : Initialize WFG_NODE
195  *
196  * returns : NO_ERROR if all OK, ER_ status otherwise
197  *
198  * node_p(out) :
199  *
200  * Note:
201  */
202 static int
203 wfg_initialize_node (WFG_NODE * node_p)
204 {
205  if (node_p == NULL)
206  {
207  return ER_FAILED;
208  }
209 
210  node_p->cycle_group_no = -1;
211  node_p->cycle_fun = NULL;
212  node_p->args = NULL;
213  node_p->first_holder_edge_p = NULL;
214  node_p->last_holder_edge_p = NULL;
215  node_p->first_waiter_edge_p = NULL;
216  node_p->last_waiter_edge_p = NULL;
217 
218  return NO_ERROR;
219 }
220 
221 /*
222  * wfg_alloc_nodes : Initialize or expand WFG to have num_trans.
223  *
224  * returns : NO_ERROR if all OK, ER_ status otherwise
225  *
226  * num_trans(IN) : number of transactions
227  *
228  * NOTE: num_trans should not be decreased in succeeding calls, otherwise,
229  * behavior undefined.
230  */
231 int
232 wfg_alloc_nodes (THREAD_ENTRY * thread_p, const int num_trans)
233 {
234  WFG_NODE *temp_node_p; /* a temporary pointer to WFG nodes */
235  int i; /* loop counter */
236  int error_code = NO_ERROR;
237 
238 #if defined(WFG_DEBUG)
239  if (num_trans < 0)
240  {
241  er_log_debug (ARG_FILE_LINE, "wfg_alloc: num_trans = %d should NOT be" " negative. Will assume 10\n" num_trans);
242  num_trans = 10;
243  }
244 #endif /* WFG_DEBUG */
245 
246  if (csect_enter (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
247  {
248  return ER_FAILED;
249  }
250 
251  /*
252  * allocate new nodes
253  */
254  if (wfg_Nodes == NULL)
255  {
256  temp_node_p = (WFG_NODE *) malloc (sizeof (WFG_NODE) * num_trans);
257  if (temp_node_p == NULL)
258  {
259  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (WFG_NODE) * num_trans);
260  error_code = ER_OUT_OF_VIRTUAL_MEMORY;
261  goto end;
262  }
263  }
264  else
265  {
266  if (num_trans < wfg_Total_nodes)
267  {
268  error_code = NO_ERROR;
269  goto end;
270  }
271  temp_node_p = (WFG_NODE *) realloc (wfg_Nodes, sizeof (WFG_NODE) * num_trans);
272  if (temp_node_p == NULL)
273  {
274  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (WFG_NODE) * num_trans);
275  error_code = ER_OUT_OF_VIRTUAL_MEMORY;
276  goto end;
277  }
278  }
279 
280  /* initialize newly allocated nodes */
281  for (i = wfg_Total_nodes; i < num_trans; i++)
282  {
283  error_code = wfg_initialize_node (temp_node_p + i);
284  if (error_code != NO_ERROR)
285  {
286  goto end;
287  }
288  }
289 
290  wfg_Total_nodes = num_trans;
291  wfg_Nodes = temp_node_p;
292 
293 end:
294  csect_exit (thread_p, CSECT_WFG);
295  return error_code;
296 }
297 
298 /*
299  * wfg_free_group_list :
300  *
301  * returns : NO_ERROR if all OK, ER_ status otherwise
302  *
303  * Note:
304  */
305 static int
306 wfg_free_group_list (void)
307 {
308  WFG_TRANS_LIST *tran_list_p, *temp_p; /* trans list pointer */
309  int i;
310 
311  /* free transaction group list */
312  for (i = 0; i < wfg_Total_tran_groups; i++)
313  {
314  for (tran_list_p = wfg_Tran_group[i].holder_tran_list_p; tran_list_p != NULL;)
315  { /* free holders */
316  temp_p = tran_list_p;
317  tran_list_p = tran_list_p->next;
318  free_and_init (temp_p);
319  }
320  for (tran_list_p = wfg_Tran_group[i].waiter_tran_list_p; tran_list_p != NULL;)
321  { /* free waiters */
322  temp_p = tran_list_p;
323  tran_list_p = tran_list_p->next;
324  free_and_init (temp_p);
325  }
326  }
327 
328  return NO_ERROR;
329 }
330 
331 /*
332  * wfg_free_nodes : Finish WFG module. The WFG area is freed.
333  *
334  * returns : NO_ERROR if all OK, ER_ status otherwise
335  *
336  */
337 int
338 wfg_free_nodes (THREAD_ENTRY * thread_p)
339 {
340  WFG_EDGE *edge_p; /* pointer to a WFG edge */
341  void *temp_p; /* temporary pointer */
342  int i; /* loop counter */
343  int error_code = NO_ERROR;
344 
345  if (csect_enter (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
346  {
347  return ER_FAILED;
348  }
349 
350  if (wfg_Total_nodes > 0)
351  {
352  /* for each node */
353  for (i = 0; i < wfg_Total_nodes; i++)
354  {
355  /* for each edge */
356  for (edge_p = wfg_Nodes[i].first_holder_edge_p; edge_p != NULL;)
357  {
358  temp_p = edge_p;
359  edge_p = edge_p->next_holder_edge_p;
360  free_and_init (temp_p);
361  }
362  }
363  free_and_init (wfg_Nodes);
364  wfg_Total_nodes = wfg_Total_edges = wfg_Total_waiters = 0;
365  }
366 
367  /* free transaction group list */
368  error_code = wfg_free_group_list ();
369 
370  csect_exit (thread_p, CSECT_WFG);
371  return error_code;
372 }
373 
374 #if defined(WFG_DEBUG)
375 static int
376 wfg_check_insert_out_edges (const int waiter_tran_index, int num_holders, const int *holder_tran_indeces)
377 {
378  int i; /* loop counter */
379  int error_code = NO_ERROR;
380 
381  /*
382  * Check for valid arguments
383  */
384  if (waiter_tran_index < 0 || waiter_tran_index > wfg_Total_nodes - 1)
385  {
387  "wfg_insert_out_edges: value" " waiter_tran_index = %d should be between 0 and %d\n"
388  " ** OPERATION HAS BEEN IGNORED **\n", waiter_tran_index, wfg_Total_nodes - 1);
389  error_code = ER_FAILED;
390  goto end;
391  }
392 
393  for (i = 0; i < num_holders; i++)
394  {
395  /* Verify for incorrect input */
396  error_code = wfg_valid_tran_index (i, holder_tran_indices[i], waiter_tran_index);
397  if (error_code != NO_ERROR)
398  {
399  goto end;
400  }
401 
402  /* check duplication of holders */
403  error_code = check_duplication_holders (i, holder_tran_indeces);
404  if (error_code != NO_ERROR)
405  {
406  goto end;
407  }
408  }
409 
410 end:
411  return error_code;
412 }
413 #endif /* WFG_DEBUG */
414 
415 /*
416  * wfg_insert_out_edges : add edges from the node specified by waiter
417  * to each node in holders.
418  *
419  * returns : NO_ERROR if all OK, ER_ status otherwise
420  *
421  * waiter_tran_index(IN) : index of transaction which is waiting
422  * num_holders(IN) : # of transactions(holders) being waited for
423  * holder_tran_indices(IN) : array of holders
424  * cycle_resolution_fn(IN) : ptr to cycle resoultion function
425  * args(IN) : arguments of cycle resolution function
426  *
427  * NOTE: indexes in waiter, holders should fall into the interval
428  * [0, num_trans of the most recent wfg_alloc_nodes()]
429  * Otherwise, behavior is undefined
430  *
431  */
432 int
433 wfg_insert_out_edges (THREAD_ENTRY * thread_p, const int waiter_tran_index, int num_holders,
434  const int *holder_tran_indeces, int (*cycle_resolution_fn) (int tran_index, void *args),
435  void *args)
436 {
437  WFG_EDGE *first_edge_p; /* pointer to the first of allocated edges */
438  WFG_EDGE *last_edge_p; /* pointer to the last of allocated edges */
439  int error_code = NO_ERROR;
440 
441  if (num_holders < 0)
442  {
443  num_holders = 0;
444  }
445 
446  if (csect_enter (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
447  {
448  return ER_FAILED;
449  }
450 
451 #if defined(WFG_DEBUG)
452  error_code = wfg_check_insert_out_edges (waiter_tran_index, num_holders, holder_tran_indeces);
453  if (error_code != NO_ERROR)
454  {
455  goto end;
456  }
457 #endif /* WFG_DEBUG */
458 
459 
460  /* allocate the edges */
461  error_code = wfg_allocate_edges (&first_edge_p, &last_edge_p, holder_tran_indeces, num_holders, waiter_tran_index);
462  if (error_code != NO_ERROR)
463  {
464  goto end;
465  }
466  /*
467  * Save the function to call in the case of a cycle.
468  */
469  wfg_Nodes[waiter_tran_index].cycle_fun = cycle_resolution_fn;
470  wfg_Nodes[waiter_tran_index].args = args;
471 
472  error_code = wfg_link_edge_holders_waiter_list (first_edge_p, last_edge_p, waiter_tran_index);
473  if (error_code != NO_ERROR)
474  {
475  goto end;
476  }
477 
478  wfg_Total_edges += num_holders;
479 
480 end:
481  csect_exit (thread_p, CSECT_WFG);
482 
483  return error_code;
484 }
485 
486 #if defined(WFG_DEBUG)
487 /*
488  * wfg_valid_tran_index :
489  *
490  * returns : NO_ERROR if all OK, ER_ status otherwise
491  *
492  * holder_index(IN) :
493  * holder_transaction_index(IN) :
494  * waiter_transaction_index(IN) :
495  */
496 static int
497 wfg_valid_tran_index (const int holder_index, const int holder_tran_index, const int waiter_tran_index)
498 {
499  if (holder_tran_index < 0 || holder_tran_index > wfg_Total_nodes - 1)
500  {
502  "wfg_insert_out_edges: value" " holder_tran_indices[%d] = %d should be between 0 and %d\n"
503  " ** OPERATION HAS BEEN IGNORED **\n", holder_index, holder_tran_index, wfg_Total_nodes - 1);
504 
505  return ER_FAILED;
506  }
507  if (holder_tran_index == waiter_tran_index)
508  {
510  "wfg_insert_out_edges: value" " holder_tran_indices[%d] = %d is equal to waiter_tran_index = %d\n"
511  " ** OPERATION HAS BEEN IGNORED **\n", holder_index, holder_tran_index, waiter_tran_index);
512 
513  return ER_FAILED;
514  }
515 
516  return NO_ERROR;
517 }
518 
519 /*
520  * check_duplication_holders :
521  *
522  * returns : NO_ERROR if all OK, ER_ status otherwise
523  *
524  * holder_index(IN) :
525  * holder_tran_indices(IN) :
526  * num_holders(IN) :
527  * waiter_tran_index(IN) :
528  *
529  */
530 static int
531 check_duplication_holders (const int holder_index, const int *holder_tran_indices, const int num_holders,
532  const int waiter_tran_index)
533 {
534  WFG_EDGE *edge_p;
535  int i;
536 
537  for (i = holder_index + 1; i < num_holders; i++)
538  {
539  if (holder_tran_indices[holder_index] == holder_tran_indices[i])
540  {
542  "wfg_insert_outedges: value holder_tran_indices[%d] = %d is"
543  " duplcated with holder_tran_indices[%d] = %d\n" "** OPERATION HAS BEEN IGNORED **\n",
544  holder_index, holder_tran_indices[holder_index], i, holder_tran_indices[i]);
545  return ER_FAILED;
546  }
547  }
548 
549  for (edge_p = wfg_Nodes[waiter_tran_index].first_holder_edge_p; edge_p != NULL; edge_p = edge_p->next_holder_edge_p)
550  {
551  if (holder_tran_indices[holder_index] == edge_p->holder_tran_index)
552  {
554  "wfg_insert_outedges: value holder_tran_indices[%d] = %d" " is already a holders\n"
555  " ** OPERATION HAS BEEN IGNORED **\n", holder_index, holder_tran_indices[holder_index]);
556  return ER_FAILED;
557  }
558  }
559 
560  return NO_ERROR;
561 }
562 #endif /* WFG_DEBUG */
563 
564 /*
565  * wfg_allocate_edges : Allocate and initialize edges to have num_holders
566  *
567  * returns : NO_ERROR if all OK, ER_ status otherwise
568  *
569  * first_edge_p(OUT) : pointer to the first of allocated edges
570  * last_edge_p(OUT) : pointer to the last of allocated edges
571  * holder_tran_indices(IN): array of holders
572  * num_holders(IN) : # of transactions(holders)
573  * waiter_tran_index(IN) : index of transaction which is waiting
574  *
575  */
576 static int
577 wfg_allocate_edges (WFG_EDGE ** first_edge_p, WFG_EDGE ** last_edge_p, const int *holder_tran_indices,
578  const int num_holders, const int waiter_tran_index)
579 {
580  WFG_EDGE *edge_p;
581  WFG_EDGE *temp_p;
582  int i;
583 
584  *first_edge_p = *last_edge_p = NULL;
585 
586  for (i = num_holders - 1; i >= 0; i--)
587  {
588  edge_p = (WFG_EDGE *) malloc (sizeof (WFG_EDGE));
589  if (edge_p == NULL)
590  {
591  /* Deallocate all edges and return a failure */
592  while (*first_edge_p != NULL)
593  {
594  temp_p = *first_edge_p;
595  *first_edge_p = (*first_edge_p)->next_holder_edge_p;
596  free_and_init (temp_p);
597  }
598 
601  }
602 
603  /*
604  * Note that we are adding the edges in the reverse order to avoid
605  * manupulating several pointers.
606  */
607  edge_p->waiter_tran_index = waiter_tran_index;
608  edge_p->holder_tran_index = holder_tran_indices[i];
609  edge_p->next_holder_edge_p = *first_edge_p;
610  edge_p->next_waiter_edge_p = NULL;
611 
612  *first_edge_p = edge_p;
613  if (*last_edge_p == NULL)
614  {
615  *last_edge_p = *first_edge_p;
616  }
617  }
618 
619  return NO_ERROR;
620 }
621 
622 /*
623  * wfg_link_edge_holders_waiter_list : Link the list to the waiter as
624  * its holders and link each edge to holders'
625  * waiter list
626  *
627  * returns : NO_ERROR if all OK, ER_ status otherwise
628  *
629  * first_edge_p(IN) : pointer to the first of allocated edges
630  * last_edge_p(IN) : pointer to the last of allocated edges
631  * waiter(IN) : index of transaction which is waiting
632  *
633  */
634 static int
635 wfg_link_edge_holders_waiter_list (WFG_EDGE * first_edge_p, WFG_EDGE * last_edge_p, const int waiter)
636 {
637  WFG_EDGE *edge_p;
638  int holder;
639 
640  /*
641  * Link the list to the waiter as its holders
642  */
643  if (first_edge_p != NULL)
644  {
645  if (wfg_Nodes[waiter].last_holder_edge_p == NULL)
646  {
647  /* First holder */
648  wfg_Nodes[waiter].first_holder_edge_p = first_edge_p;
649  wfg_Total_waiters++;
650  }
651  else
652  {
653  wfg_Nodes[waiter].last_holder_edge_p->next_holder_edge_p = first_edge_p;
654  }
655  wfg_Nodes[waiter].last_holder_edge_p = last_edge_p;
656  }
657 
658  /*
659  * Link each edge to holders' waiter list
660  */
661  for (edge_p = first_edge_p; edge_p; edge_p = edge_p->next_holder_edge_p)
662  {
663  holder = edge_p->holder_tran_index;
664  if (wfg_Nodes[holder].last_waiter_edge_p == NULL)
665  {
666  /* First waiter */
667  wfg_Nodes[holder].first_waiter_edge_p = edge_p;
668  }
669  else
670  {
671  wfg_Nodes[holder].last_waiter_edge_p->next_waiter_edge_p = edge_p;
672  }
673  wfg_Nodes[holder].last_waiter_edge_p = edge_p;
674  }
675 
676  return NO_ERROR;
677 }
678 
679 #if defined(WFG_DEBUG)
680 static int
681 wfg_check_remove_out_edges (const int waiter_tran_index, const int num_holders, const int *holder_tran_indices_p)
682 {
683  int error_code = NO_ERROR;
684  int i;
685  WFG_EDGE *edge_p; /* An edge */
686 
687  /*
688  * Check for valid arguments
689  */
690  if (waiter_tran_index < 0 || waiter_tran_index > wfg_Total_nodes - 1)
691  {
693  "wfg_remove_outedges: value waiter = %d should"
694  " be between 0 and %d\n ** OPERATION HAS BEEN IGNORED **\n", waiter_tran_index,
695  wfg_Total_nodes - 1);
696 
697  error_code = ER_FAILED;
698  goto end;
699  }
700 
701  for (i = 0; i < num_holders; i++)
702  {
703  if (holder_tran_indices_p[i] < 0 || holder_tran_indices_p[i] > wfg_Total_nodes - 1)
704  {
706  "wfg_remove_outedges: value holder[%d] = %d"
707  " should be between 0 and %d\n ** OPERATION HAS BEEN" " IGNORED **", i, htran_indices_p[i],
708  wfg_Total_nodes - 1);
709  error_code = ER_FAILED;
710  goto end;
711  }
712  for (edge_p = wfg_Nodes[waiter_tran_index].first_holder_edge_p; edge_p != NULL;
713  edge_p = edge_p->next_holder_edge_p)
714  {
715  if (holder_tran_indices_p[i] == edge_p->holder_tran_index)
716  {
718  "wfg_remove_outedges:" " value holder[%d] = %d is NOT holder of waiter = %d\n"
719  " ** THE HOLDER SKIPPED **", i, htran_indices_p[i], waiter_tran_index);
720  }
721  }
722  }
723 
724  return error_code;
725 }
726 #endif /* WFG_DEBUG */
727 
728 /*
729  * wfg_remove_waiter_list_of_holder_edge :
730  *
731  * returns : NO_ERROR if all OK, ER_ status otherwise
732  *
733  * node_p(in/out):
734  * holder_p(in/out): pointer to prev. holder edge
735  *
736  */
737 static int
738 wfg_remove_waiter_list_of_holder_edge (WFG_NODE * node_p, WFG_EDGE ** holder_p)
739 {
740  WFG_EDGE **waiter_p; /* pointer to prev. waiter edge */
741 
742  for (waiter_p = &(node_p->first_waiter_edge_p); *waiter_p != NULL; waiter_p = &((*waiter_p)->next_waiter_edge_p))
743  {
744  if (*waiter_p == *holder_p)
745  {
746  /* It has been found. Now remove it from the waiter list */
747  *waiter_p = (*waiter_p)->next_waiter_edge_p;
748  if (*waiter_p == NULL)
749  {
750  /* the last waiter of this holder edge */
751  if (node_p->first_waiter_edge_p == NULL)
752  {
753  /* No more waiters */
754  node_p->last_waiter_edge_p = NULL;
755  }
756  else
757  {
758  /* There are still waiters */
759  node_p->last_waiter_edge_p =
760  (WFG_EDGE *) ((char *) waiter_p - offsetof (WFG_EDGE, next_waiter_edge_p));
761  }
762  }
763  break;
764  }
765  }
766 
767  return NO_ERROR;
768 }
769 
770 /*
771  * wfg_remove_out_edges : remove edges from the node waiter_tran_index
772  * to each node in holders.
773  * If num_holders <= 0 or holders is NULL, it removes all
774  * outgoing edges of the node waiter_tran_index.
775  *
776  * returns : NO_ERROR if all OK, ER_ status otherwise
777  *
778  * waiter_tran_index(IN) : index of transaction which is waiting
779  * num_holders(IN) : # of transactions(holders)
780  * holder_tran_indices_p(IN): array of holders
781  *
782  * NOTE: indexes in waiter, holders should fall into the interval
783  * [0, num_trans of the most recent wfg_alloc_nodes()]
784  * Otherwise, behavior is undefined
785  *
786  */
787 int
788 wfg_remove_out_edges (THREAD_ENTRY * thread_p, const int waiter_tran_index, const int num_holders,
789  const int *holder_tran_indices_p)
790 {
791  int i; /* loop counter */
792  WFG_EDGE *edge_p; /* An edge */
793  WFG_EDGE **prev_holder_p; /* pointer to prev. holder edge */
794  int error_code = NO_ERROR;
795 
796  if (csect_enter (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
797  {
798  return ER_FAILED;
799  }
800 
801 #if defined(WFG_DEBUG)
802  error_code = wfg_check_remove_out_edges (waiter_tran_index, num_holders, holder_tran_indices_p);
803  if (error_code != NO_ERROR)
804  {
805  goto end;
806  }
807 #endif /* WFG_DEBUG */
808  for (prev_holder_p = &(wfg_Nodes[waiter_tran_index].first_holder_edge_p); *prev_holder_p != NULL;)
809  {
810  /* check if the edge is in the given edges */
811  if (num_holders > 0 && holder_tran_indices_p != NULL)
812  {
813  for (i = 0; i < num_holders; i++)
814  {
815  if (holder_tran_indices_p[i] == (*prev_holder_p)->holder_tran_index)
816  {
817  break;
818  }
819  }
820  }
821  else
822  {
823  i = num_holders - 1;
824  }
825 
826  if (i < num_holders)
827  {
828  /*
829  * It was found, remove previous holder from the list of waiters
830  * and the holder list.
831  */
832 
833  /* Remove from waiter list of the holder of edge */
834  error_code =
835  wfg_remove_waiter_list_of_holder_edge (&wfg_Nodes[(*prev_holder_p)->holder_tran_index], prev_holder_p);
836  if (error_code != NO_ERROR)
837  {
838  goto end;
839  }
840 
841  /* Remove from holder list of the waiter */
842  edge_p = *prev_holder_p;
843  *prev_holder_p = (*prev_holder_p)->next_holder_edge_p;
844  free_and_init (edge_p);
845  wfg_Total_edges--;
846  }
847  else
848  {
849  prev_holder_p = &((*prev_holder_p)->next_holder_edge_p);
850  }
851  }
852 
853  if (prev_holder_p == &(wfg_Nodes[waiter_tran_index].first_holder_edge_p))
854  {
855  /* all outgoing edges are removed */
856  wfg_Nodes[waiter_tran_index].last_holder_edge_p = NULL;
857  wfg_Total_waiters--;
858  }
859  else
860  {
861  wfg_Nodes[waiter_tran_index].last_holder_edge_p =
862  (WFG_EDGE *) ((char *) prev_holder_p - offsetof (WFG_EDGE, next_holder_edge_p));
863  }
864 
865 end:
866  csect_exit (thread_p, CSECT_WFG);
867  return error_code;
868 }
869 
870 /*
871  * wfg_get_status : get statistic from WFG.
872  *
873  * returns : NO_ERROR if all OK, ER status otherwise
874  *
875  * edges(OUT) : pointer to room to store # of edges
876  * waiters(OUT) : pointer to room to store # of waiting trans
877  *
878  */
879 int
880 wfg_get_status (int *num_edges_p, int *num_waiters_p)
881 {
882  assert (num_edges_p != NULL);
883  assert (num_waiters_p != NULL);
884 
885  *num_edges_p = wfg_Total_edges;
886  *num_waiters_p = wfg_Total_waiters;
887 
888  return NO_ERROR;
889 }
890 
891 /*
892  * wfg_detect_cycle : finds all elementary cycles in the WFG and
893  * transaction groups.
894  *
895  * returns : NO_ERROR if all OK, ER status otherwise
896  *
897  * cycle_case(OUT) : cycle_case.. One of the following values:
898  * WFG_CYCLE_YES_PRUNE
899  * WFG_CYCLE_YES
900  * WFG_CYCLE_NO
901  * WFG_CYCLE_ERROR
902  * list_cycles_p(IN/OUT) : address to list of cycles
903  * Cycles is set as a side effect to point to list of
904  * cycles.
905  *
906  * NOTE: the caller should be responsible for freeing memory allocated to the
907  * cycles after usage. See wfg_free_cycle()
908  */
909 int
910 wfg_detect_cycle (THREAD_ENTRY * thread_p, WFG_CYCLE_CASE * cycle_case, WFG_CYCLE ** list_cycles_p)
911 {
912  /* LIMIT the number of cycles */
913  return wfg_internal_detect_cycle (thread_p, cycle_case, list_cycles_p, WFG_PRUNE_CYCLES_IN_CYCLE_GROUP,
914  WFG_MAX_CYCLES_TO_REPORT);
915 }
916 
917 /*
918  * wfg_internal_detect_cycle() : finds all elementary cycles in the WFG and
919  * transaction groups.
920  *
921  * returns : NO_ERROR if all OK, ER status otherwise
922  *
923  * cycle_case(OUT) : cycle_case.. One of the following values:
924  * WFG_CYCLE_YES_PRUNE
925  * WFG_CYCLE_YES
926  * WFG_CYCLE_NO
927  * WFG_CYCLE_ERROR
928  * list_cycles_p(IN/OUT) : address to list of cycles
929  * Cycles is set as a side effect to point to list of
930  * cycles.
931  * max_cycles_in_cycle_group(IN) : Prune the number of found cycles
932  * in a cycle group
933  * max_cycles(IN) : max cycles to report
934  *
935  */
936 static int
937 wfg_internal_detect_cycle (THREAD_ENTRY * thread_p, WFG_CYCLE_CASE * cycle_case_p, WFG_CYCLE ** list_cycles_p,
938  const int max_cycles_in_cycle_group, const int max_cycles)
939 {
940  WFG_CYCLE **next_cycle_p; /* address of pointer to next cycle */
941  WFG_CYCLE *ordinary_cycles_p = NULL; /* ordinary cycles */
942  WFG_CYCLE *tran_group_cycles_p = NULL; /* TG(transaction group) cycles */
943  WFG_CYCLE_CASE cycle_tran_group_case;
944  int error_code = NO_ERROR;
945 
946  *list_cycles_p = NULL;
947 
948  error_code =
949  wfg_detect_ordinary_cycle (thread_p, cycle_case_p, &ordinary_cycles_p, max_cycles_in_cycle_group, max_cycles);
950  if (error_code != NO_ERROR)
951  {
952  goto error;
953  }
954 
955  if (ordinary_cycles_p != NULL)
956  {
957  *list_cycles_p = ordinary_cycles_p;
958  }
959 
960  error_code = wfg_detect_tran_group_cycle (thread_p, &cycle_tran_group_case, &tran_group_cycles_p);
961  if (error_code != NO_ERROR)
962  {
963  goto error;
964  }
965 
966  if (tran_group_cycles_p != NULL)
967  {
968  /* Glue the lists */
969  for (next_cycle_p = list_cycles_p; *next_cycle_p != NULL; next_cycle_p = &((*next_cycle_p)->next))
970  {
971  ; /* NO-OP */
972  }
973  *next_cycle_p = tran_group_cycles_p;
974  }
975 
976  switch (*cycle_case_p)
977  {
978  case WFG_CYCLE_YES_PRUNE:
979  break;
980 
981  case WFG_CYCLE_YES:
982  if (cycle_tran_group_case == WFG_CYCLE_YES_PRUNE)
983  {
984  *cycle_case_p = cycle_tran_group_case;
985  }
986  break;
987 
988  case WFG_CYCLE_NO:
989  *cycle_case_p = cycle_tran_group_case;
990  break;
991 
992  default:
993  break;
994  }
995 
996  return NO_ERROR;
997 
998 error:
999 
1000  /* free allocated cycles */
1001  if (ordinary_cycles_p != NULL)
1002  {
1003  wfg_free_cycle (ordinary_cycles_p);
1004  }
1005 
1006  if (tran_group_cycles_p != NULL)
1007  {
1008  wfg_free_cycle (tran_group_cycles_p);
1009  }
1010 
1011  *list_cycles_p = NULL;
1012 
1013  *cycle_case_p = WFG_CYCLE_ERROR;
1014 
1015  return ER_FAILED;
1016 }
1017 
1018 /*
1019  * wfg_free_cycle : free memory allocated to store cycles by wfg_detect_cycle().
1020  *
1021  * return : NO_ERROR if all OK, ER status otherwise
1022  *
1023  * list_cycles(IN/OUT) : address to list of cycles
1024  *
1025  */
1026 int
1027 wfg_free_cycle (WFG_CYCLE * list_cycles_p)
1028 {
1029  WFG_CYCLE *current_cycle_p; /* temp pointer to a WFG_CYCLE node */
1030  WFG_CYCLE *temp_p; /* temp variable for current_cycle_p */
1031 
1032  if (list_cycles_p != NULL)
1033  {
1034  for (current_cycle_p = list_cycles_p; current_cycle_p != NULL;)
1035  {
1036  if (current_cycle_p->waiters != NULL)
1037  {
1038  free_and_init (current_cycle_p->waiters);
1039  }
1040 
1041  temp_p = current_cycle_p;
1042  current_cycle_p = current_cycle_p->next;
1043  free_and_init (temp_p);
1044  }
1045  }
1046 
1047  return NO_ERROR;
1048 }
1049 
1050 /*
1051  * wfg_print_given_cycles:
1052  *
1053  * return : NO_ERROR if all OK, ER status otherwise
1054  *
1055  * out_fp(in) : out file
1056  * list_cycles_p(IN) : address to list of cycles
1057  *
1058  */
1059 static int
1060 wfg_dump_given_cycles (FILE * out_fp, WFG_CYCLE * list_cycles_p)
1061 {
1062  int i;
1063  WFG_CYCLE *current_cycle_p;
1064 
1065  fprintf (out_fp, "----------------- CYCLES ------------------\n");
1066 
1067  /*
1068  * There are deadlocks, we must select a victim for each cycle. We try
1069  * to break a cycle by timeing out a transaction whenever is possible.
1070  * In any other case, we select a victim for an unilaterally abort.
1071  */
1072 
1073  for (current_cycle_p = list_cycles_p; current_cycle_p != NULL; current_cycle_p = current_cycle_p->next)
1074  {
1075  fprintf (out_fp, "Cycle: ");
1076  for (i = 0; i < current_cycle_p->num_trans; i++)
1077  {
1078  if (i > 0)
1079  {
1080  fprintf (out_fp, ", ");
1081  if ((i % 10) == 0)
1082  {
1083  fprintf (out_fp, "\n ");
1084  }
1085  }
1086  fprintf (out_fp, "%d", current_cycle_p->waiters[i].tran_index);
1087  }
1088  fprintf (out_fp, "\n");
1089  }
1090 
1091  return NO_ERROR;
1092 }
1093 
1094 /*
1095  * wfg_dump_holder_waiter:
1096  *
1097  * return : NO_ERROR if all OK, ER status otherwise
1098  *
1099  * out_fp(in) : out file
1100  * node_index(in):
1101  *
1102  */
1103 static void
1104 wfg_dump_holder_waiter (FILE * out_fp, int node_index)
1105 {
1106  WFG_EDGE *edge_p;
1107 
1108  /* Print holders of node */
1109  fprintf (out_fp, "\t holders = { ");
1110  for (edge_p = wfg_Nodes[node_index].first_holder_edge_p; edge_p != NULL; edge_p = edge_p->next_holder_edge_p)
1111  {
1112  fprintf (out_fp, "%03d ", edge_p->holder_tran_index);
1113  }
1114  fprintf (out_fp, "}\n");
1115 
1116  /* Print waiters of node */
1117  fprintf (out_fp, "\t\t waiters = { ");
1118  for (edge_p = wfg_Nodes[node_index].first_waiter_edge_p; edge_p != NULL; edge_p = edge_p->next_waiter_edge_p)
1119  {
1120  fprintf (out_fp, "%03d ", edge_p->waiter_tran_index);
1121  }
1122  fprintf (out_fp, "}\n");
1123 
1124  if (wfg_Nodes[node_index].last_holder_edge_p == NULL)
1125  {
1126  fprintf (out_fp, "\t\t last holder = null,");
1127  }
1128  else
1129  {
1130  fprintf (stdout, "\t\t last holder = %03d,", wfg_Nodes[node_index].last_holder_edge_p->holder_tran_index);
1131  }
1132 
1133  if (wfg_Nodes[node_index].last_waiter_edge_p == NULL)
1134  {
1135  fprintf (out_fp, "\t\t last waiter = null\n");
1136  }
1137  else
1138  {
1139  fprintf (out_fp, "\t\t last waiter = %03d\n", wfg_Nodes[node_index].last_waiter_edge_p->waiter_tran_index);
1140  }
1141 }
1142 
1143 /*
1144  * wfg_dump_holder_waiter_of_tran_group:
1145  *
1146  * return : NO_ERROR if all OK, ER status otherwise
1147  *
1148  * out_fp(in) : out file
1149  * group_index(in):
1150  *
1151  */
1152 static void
1153 wfg_dump_holder_waiter_of_tran_group (FILE * out_fp, int group_index)
1154 {
1155  WFG_TRANS_LIST *tran_list_p;
1156 
1157  if (wfg_Tran_group[group_index].holder_tran_list_p)
1158  {
1159  /* Print holders of TG */
1160  fprintf (out_fp, "\t holders = { ");
1161  for (tran_list_p = wfg_Tran_group[group_index].holder_tran_list_p; tran_list_p != NULL;
1162  tran_list_p = tran_list_p->next)
1163  {
1164  fprintf (out_fp, "%d ", tran_list_p->tran_index);
1165  }
1166  fprintf (out_fp, "}\n");
1167 
1168  if (wfg_Tran_group[group_index].waiter_tran_list_p)
1169  {
1170  /* Print waiters of TG */
1171  fprintf (out_fp, "\t waiters = { ");
1172  for (tran_list_p = wfg_Tran_group[group_index].waiter_tran_list_p; tran_list_p != NULL;
1173  tran_list_p = tran_list_p->next)
1174  {
1175  fprintf (out_fp, "%d ", tran_list_p->tran_index);
1176  }
1177  fprintf (out_fp, "}\n");
1178  }
1179  }
1180 }
1181 
1182 /*
1183  * wfg_dump :
1184  *
1185  * return : NO_ERROR if all OK, ER status otherwise
1186  *
1187  */
1188 int
1189 wfg_dump (THREAD_ENTRY * thread_p)
1190 {
1191  int i;
1192  WFG_CYCLE *cycles_p;
1193  WFG_CYCLE_CASE cycle_case;
1194 
1195  fprintf (stdout, "--------------- WFG contents --------------\n");
1196  fprintf (stdout, "total_nodes = %d, total_edges = %d, total_waiters = %d\n", wfg_Total_nodes, wfg_Total_edges,
1197  wfg_Total_waiters);
1198 
1199  fprintf (stdout, "\n");
1200  fprintf (stdout, "---------- Ordinary WFG contents ----------\n");
1201 
1202  for (i = 0; i < wfg_Total_nodes; i++)
1203  {
1204  fprintf (stdout, "[node_%03d]:", i);
1205  wfg_dump_holder_waiter (stdout, i);
1206  }
1207 
1208  if (wfg_Total_tran_groups > 0)
1209  {
1210  fprintf (stdout, "\n");
1211  fprintf (stdout, "------------- WFG_TG contents -------------\n");
1212 
1213  for (i = 0; i < wfg_Total_tran_groups; i++)
1214  {
1215  fprintf (stdout, "TG[%d]:\t Num_holders %d, Num_waiters %d\n", i, wfg_Tran_group[i].num_holders,
1216  wfg_Tran_group[i].num_waiters);
1217 
1218  wfg_dump_holder_waiter_of_tran_group (stdout, i);
1219  }
1220  }
1221 
1222  /* Dump all cycles that are currently involved in the system */
1223  if (wfg_internal_detect_cycle (thread_p, &cycle_case, &cycles_p, -1, -1) == NO_ERROR && cycle_case == WFG_CYCLE_YES)
1224  {
1225  fprintf (stdout, "\n");
1226  wfg_dump_given_cycles (stdout, cycles_p);
1227  wfg_free_cycle (cycles_p);
1228  }
1229 
1230  return NO_ERROR;
1231 }
1232 
1233 /*
1234  * wfg_alloc_tran_group :
1235  *
1236  * return : if success, non-negative Transaction Group entry index,
1237  * otherwise, ER_FAILED value
1238  *
1239  */
1240 int
1241 wfg_alloc_tran_group (THREAD_ENTRY * thread_p)
1242 {
1243  WFG_TRAN_GROUP *temp_p; /* temp_p pointer to new TG table */
1244  size_t bytes; /* size of new TG table */
1245  int error_code = NO_ERROR;
1246 
1247  if (csect_enter (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
1248  {
1249  return ER_FAILED;
1250  }
1251 
1252  bytes = sizeof (WFG_TRAN_GROUP) * (wfg_Total_tran_groups + 1);
1253  if (wfg_Total_tran_groups == 0)
1254  {
1255  temp_p = (WFG_TRAN_GROUP *) malloc (bytes);
1256  }
1257  else
1258  {
1259  temp_p = (WFG_TRAN_GROUP *) realloc (wfg_Tran_group, bytes);
1260  }
1261 
1262  if (temp_p == NULL)
1263  {
1265  error_code = ER_OUT_OF_VIRTUAL_MEMORY;
1266  goto end;
1267  }
1268 
1269  wfg_Tran_group = temp_p;
1270 
1271  /* initialize the newly allocated entry */
1272  wfg_Tran_group[wfg_Total_tran_groups].num_holders = 0;
1273  wfg_Tran_group[wfg_Total_tran_groups].num_waiters = 0;
1274  wfg_Tran_group[wfg_Total_tran_groups].holder_tran_list_p = NULL;
1275  wfg_Tran_group[wfg_Total_tran_groups].waiter_tran_list_p = NULL;
1276 
1277  wfg_Total_tran_groups++;
1278 
1279 end:
1280  csect_exit (thread_p, CSECT_WFG);
1281 
1282  return (wfg_Total_tran_groups - 1);
1283 }
1284 
1285 /*
1286  * wfg_insert_holder_tran_group : register the tran_index as a holder
1287  * of the Transaction Group tran_group_index.
1288  *
1289  * return : NO_ERROR if all OK, ER status otherwise
1290  *
1291  * tran_group_index(IN) : Transaction Group entry index
1292  * holder_tran_index(IN) : tran_index to be entered
1293  *
1294  * NOTE: the behavior on invalid tran_group_index and holder_tran_index,
1295  * is undefined.
1296  *
1297  */
1298 int
1299 wfg_insert_holder_tran_group (THREAD_ENTRY * thread_p, const int tran_group_index, const int holder_tran_index)
1300 {
1301  WFG_TRANS_LIST *tran_list_p; /* temp trans list node pointer */
1302  int error_code = NO_ERROR;
1303 
1304  /* Create a node for the tran_index and insert it to the TG's holder list */
1305  tran_list_p = (WFG_TRANS_LIST *) malloc (sizeof (WFG_TRANS_LIST));
1306  if (tran_list_p == NULL)
1307  {
1308  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (WFG_TRANS_LIST));
1309  return ER_OUT_OF_VIRTUAL_MEMORY;
1310  }
1311 
1312  if (csect_enter (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
1313  {
1314  free_and_init (tran_list_p);
1315  return ER_FAILED;
1316  }
1317 
1318 #if defined(WFG_DEBUG)
1319  if (tran_group_index < 0 || tran_group_index > wfg_Total_tran_groups - 1)
1320  {
1322  "wfg_tg_insert_holder: value tg_index = %d" " should be between 0 and %d\n"
1323  "** OPERATION HAS BEEN IGNORED **", tran_group_index, wfg_Total_tran_groups - 1);
1324  error_code = ER_FAILED;
1325  goto end;
1326  }
1327 
1328  if (holder_tran_index < 0 || holder_tran_index > wfg_Total_nodes - 1)
1329  {
1331  "wfg_tg_insert_holder: value htran_index = %d" " should be between 0 and %d\n"
1332  " ** OPERATION HAS BEEN IGNORED **", holder_tran_index, wfg_Total_nodes - 1);
1333  error_code = ER_FAILED;
1334  goto end;
1335  }
1336 
1337  for (tran_list_p = wfg_Tran_group[tran_group_index].holder_tran_list_p; tran_list_p != NULL;
1338  tran_list_p = tran_list_p->next)
1339  if (tran_list_p->tran_index == holder_tran_index)
1340  {
1342  "wfg_tg_insert_holder: value" " htran_index = %d is already in holder list\n"
1343  " ** OPERATION HAS BEEN IGNORED **", tran_index);
1344  error_code = ER_FAILED;
1345  goto end;
1346  }
1347 #endif /* WFG_DEBUG */
1348 
1349  tran_list_p->tran_index = holder_tran_index;
1350  tran_list_p->next = wfg_Tran_group[tran_group_index].holder_tran_list_p;
1351  wfg_Tran_group[tran_group_index].holder_tran_list_p = tran_list_p;
1352  wfg_Tran_group[tran_group_index].num_holders++;
1353 
1354 #if defined(WFG_DEBUG)
1355 end:
1356 #endif /* WFG_DEBUG */
1357 
1358  csect_exit (thread_p, CSECT_WFG);
1359 
1360  return error_code;
1361 }
1362 
1363 /*
1364  * wfg_remove_holder_tran_group : delete holder_tran_index from the holder list
1365  * of Transaction Group tran_group_index
1366  *
1367  * return : NO_ERROR if all OK, ER status otherwise
1368  *
1369  * tran_group_index(IN) : Transaction Group entry index
1370  * holder_tran_index(IN) : tran_index to be removed
1371  *
1372  * NOTE: the behavior on invalid tran_group_index and holder_tran_index,
1373  * is undefined.
1374  *
1375  */
1376 int
1377 wfg_remove_holder_tran_group (THREAD_ENTRY * thread_p, const int tran_group_index, const int holder_tran_index)
1378 {
1379  WFG_TRANS_LIST **tran_list_p; /* transaction list node */
1380  WFG_TRANS_LIST *temp_p;
1381  int error_code = NO_ERROR;
1382 
1383  if (csect_enter (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
1384  {
1385  return ER_FAILED;
1386  }
1387 
1388 #if defined(WFG_DEBUG)
1389  if (tran_group_index < 0 || tran_group_index > wfg_Total_tran_groups - 1)
1390  {
1392  "wfg_tg_remove_holder: value tg_index = %d" " should be between 0 and %d\n"
1393  " ** OPERATION HAS BEEN IGNORED **", tran_group_index, wfg_Total_tran_groups - 1);
1394  error_code = ER_FAILED;
1395  goto end;
1396  }
1397 
1398  if (holder_tran_index < 0 || holder_tran_index > wfg_Total_nodes - 1)
1399  {
1401  "wfg_tg_remove_holder: value htran_index = %d" " should be between 0 and %d\n"
1402  " ** OPERATION HAS BEEN IGNORED **", holder_tran_index, wfg_Total_nodes - 1);
1403  error_code = ER_FAILED;
1404  goto end;
1405  }
1406 
1407  for (temp_p = wfg_Tran_group[tran_group_index].holder_tran_list_p; temp_p != NULL; temp_p = temp_p->next)
1408  {
1409  if (temp_p->tran_index == holder_tran_index)
1410  {
1412  "wfg_tg_remove_holder: value" " htran_index = %d is NOT in holder list\n"
1413  " ** OPERATION HAS NO EFFECT **", holder_tran_index);
1414  error_code = ER_FAILED;
1415  goto end;
1416  }
1417  }
1418 #endif /* WFG_DEBUG */
1419 
1420  if (wfg_Tran_group[tran_group_index].holder_tran_list_p != NULL)
1421  {
1422  for (tran_list_p = &wfg_Tran_group[tran_group_index].holder_tran_list_p; *tran_list_p != NULL;
1423  tran_list_p = &((*tran_list_p)->next))
1424  {
1425  if ((*tran_list_p)->tran_index == holder_tran_index)
1426  {
1427  /* Remove it, and change the pointer in the previous structure */
1428  temp_p = *tran_list_p;
1429  *tran_list_p = (*tran_list_p)->next;
1430  free_and_init (temp_p);
1431  wfg_Tran_group[tran_group_index].num_holders--;
1432  break;
1433  }
1434  }
1435  }
1436 
1437 #if defined(WFG_DEBUG)
1438 end:
1439 #endif /* WFG_DEBUG */
1440 
1441  csect_exit (thread_p, CSECT_WFG);
1442 
1443  return error_code;
1444 }
1445 
1446 /*
1447  * wfg_insert_waiter_tran_group : register the tran_index as a waiter
1448  * of the Transaction Group tran_group_index.
1449  *
1450  * return : NO_ERROR if all OK, ER status otherwise
1451  *
1452  * tran_group_index(IN) : Transaction Group entry index
1453  * waiter_tran_index(IN) : tran_index to be entered
1454  * cycle_resolution_fn(IN) :
1455  * args(IN) :
1456  *
1457  * NOTE: the behavior on invalid tg_index and tran_index, is undefined.
1458  *
1459  * The implementation of this function is almost identical as that of
1460  * wfg_insert_holder_tran_group(). The only difference is that this function
1461  * replaces holder by waiter.
1462  *
1463  */
1464 int
1465 wfg_insert_waiter_tran_group (THREAD_ENTRY * thread_p, const int tran_group_index, const int waiter_tran_index,
1466  int (*cycle_resolution_fn) (int tran_index, void *args), void *args)
1467 {
1468  WFG_TRANS_LIST *tran_list_p; /* temp trans list node pointer */
1469  int error_code = NO_ERROR;
1470 
1471  if (csect_enter (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
1472  {
1473  return ER_FAILED;
1474  }
1475 
1476 #if defined(WFG_DEBUG)
1477  if (tran_group_index < 0 || tran_group_index > wfg_Total_tran_groups - 1)
1478  {
1480  "wfg_tg_insert_waiter: value tg_index = %d should"
1481  " be between 0 and %d\n ** OPERATION HAS BEEN IGNORED **", tran_group_index,
1482  wfg_Total_tran_groups - 1);
1483  error_code = ER_FAILED;
1484  goto end;
1485  }
1486 
1487  if (waiter_tran_index < 0 || waiter_tran_index > wfg_Total_nodes - 1)
1488  {
1490  "wfg_tg_insert_waiter: value tran_index = %d" " should be between 0 and %d\n"
1491  " ** OPERATION HAS BEEN IGNORED **", waiter_tran_index, wfg_Total_nodes - 1);
1492  error_code = ER_FAILED;
1493  goto end;
1494  }
1495 
1496  for (tran_list_p = wfg_Tran_group[tran_group_index].waiter_tran_list_p; tran_list_p != NULL;
1497  tran_list_p = tran_list_p->next)
1498  {
1499  if (tran_list_p->tran_index == waiter_tran_index)
1500  {
1502  "wfg_tg_insert_waiter: value tran_index = %d" " is already in waiters\n"
1503  " ** OPERATION HAS BEEN IGNORED **", waiter_tran_index);
1504  error_code = ER_FAILED;
1505  goto end;
1506  }
1507  }
1508 #endif /* WFG_DEBUG */
1509 
1510  /*
1511  * allocate a node for the waiter_tran_index and insert it to the TG's waiter
1512  * list
1513  */
1514  tran_list_p = (WFG_TRANS_LIST *) malloc (sizeof (WFG_TRANS_LIST));
1515  if (tran_list_p == NULL)
1516  {
1517  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (WFG_TRANS_LIST));
1518  error_code = ER_OUT_OF_VIRTUAL_MEMORY;
1519  goto end;
1520  }
1521 
1522  tran_list_p->tran_index = waiter_tran_index;
1523  tran_list_p->next = wfg_Tran_group[tran_group_index].waiter_tran_list_p;
1524  wfg_Tran_group[tran_group_index].waiter_tran_list_p = tran_list_p;
1525  wfg_Tran_group[tran_group_index].num_waiters++;
1526 
1527  /* Save the function to call in the case of a cycle. */
1528  wfg_Nodes[waiter_tran_index].cycle_fun = cycle_resolution_fn;
1529  wfg_Nodes[waiter_tran_index].args = args;
1530 
1531 end:
1532  csect_exit (thread_p, CSECT_WFG);
1533 
1534  return error_code;
1535 }
1536 
1537 /*
1538  * wfg_remove_waiter_tran_group : delete waiter tran_index from the waiter list
1539  * of Transaction Group tran_group_index
1540  *
1541  * return : return : NO_ERROR if all OK, ER status otherwise
1542  *
1543  * tg_index(IN) : Transaction Group entry index
1544  * waiter_tran_index(IN) : tran_index to be removed
1545  *
1546  * NOTE: the behavior on invalid tran_group_index and waiter_tran_index,
1547  * is undefined.
1548  *
1549  * The implementation of this function is almost identical as that of
1550  * wfg_remove_holder_tran_group(). The only difference is that this function
1551  * replaces holder by waiter.
1552  *
1553  */
1554 int
1555 wfg_remove_waiter_tran_group (THREAD_ENTRY * thread_p, const int tran_group_index, const int waiter_tran_index)
1556 {
1557  WFG_TRANS_LIST **tran_list_p; /* tran_index list node */
1558  WFG_TRANS_LIST *temp_p;
1559  int error_code = NO_ERROR;
1560 
1561  if (csect_enter (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
1562  {
1563  return ER_FAILED;
1564  }
1565 
1566 #if defined(WFG_DEBUG)
1567  if (tran_group_index < 0 || tran_group_index > wfg_Total_tran_groups - 1)
1568  {
1570  "wfg_tg_remove_waiter: value tg_index = %d should"
1571  " be between 0 and %d\n ** OPERATION HAS BEEN IGNORED **", tran_group_index,
1572  wfg_Total_tran_groups - 1);
1573  error_code = ER_FAILED;
1574  goto end;
1575  }
1576 
1577  if (waiter_tran_index < 0 || waiter_tran_index > wfg_Total_nodes - 1)
1578  {
1580  "wfg_tg_remove_waiter: value tran_index = %d" " should be between 0 and %d\n"
1581  " ** OPERATION HAS BEEN IGNORED **", waiter_tran_index, wfg_Total_nodes - 1);
1582  error_code = ER_FAILED;
1583  goto end;
1584  }
1585 
1586  for (tran_list_p = &wfg_Tran_group[tran_group_index].waiter_tran_list_p; tran_list_p != NULL;
1587  tran_list_p = &((*tran_list_p)->next))
1588  {
1589  if ((*tran_list_p)->tran_index == waiter_tran_index)
1590  {
1592  "wfg_tg_remove_waiter: value tran_index = %d"
1593  " is NOT in waiters\n ** OPERATION HAS NO EFFECT **", waiter_tran_index);
1594  error_code = ER_FAILED;
1595  goto end;
1596  }
1597  }
1598 #endif /* WFG_DEBUG */
1599 
1600  for (tran_list_p = &wfg_Tran_group[tran_group_index].waiter_tran_list_p; tran_list_p != NULL;
1601  tran_list_p = &((*tran_list_p)->next))
1602  {
1603  if ((*tran_list_p)->tran_index == waiter_tran_index)
1604  {
1605  /* Remove it */
1606  temp_p = *tran_list_p;
1607  *tran_list_p = (*tran_list_p)->next;
1608  free_and_init (temp_p);
1609  wfg_Tran_group[tran_group_index].num_waiters--;
1610  break;
1611  }
1612  }
1613 
1614 #if defined(WFG_DEBUG)
1615 end:
1616 #endif /* WFG_DEBUG */
1617 
1618  csect_exit (thread_p, CSECT_WFG);
1619 
1620  return error_code;
1621 }
1622 
1623 /*
1624  * wfg_detect_ordinary_cycle :finds elementary cycles in the ordinary WFG. I.e.,
1625  * these cycles does not include TG related ones.
1626  * The function may prune its search for finding more cycles
1627  * when many has been found. This is done for space and time
1628  * efficiency. The caller has the option to call again after
1629  * cleaning some of the dependencies (e.g., aborting some of the
1630  * transactions).
1631  *
1632  * returns : NO_ERROR if all OK, ER status otherwise
1633  *
1634  * cycle_case(OUT) : cycle_case.. One of the following values:
1635  * WFG_CYCLE_YES_PRUNE
1636  * WFG_CYCLE_YES
1637  * WFG_CYCLE_NO
1638  * WFG_CYCLE_ERROR
1639  * list_cycles_p(IN/OUT) : address to list of cycles, list_cycles_p is set
1640  * as a side effect to point to list of cycles.
1641  * max_cycles_in_group(IN) :
1642  * max_cycles(IN) :
1643  *
1644  * Note: the caller should be responsible for freeing memory allocated
1645  * to the cycles after usage. See wfg_free_cycle()
1646  */
1647 static int
1648 wfg_detect_ordinary_cycle (THREAD_ENTRY * thread_p, WFG_CYCLE_CASE * cycle_case_p, WFG_CYCLE ** list_cycles_p,
1649  const int max_cycles_in_group, const int max_cycles)
1650 {
1651  int i, j;
1652  WFG_CYCLE **last_cycle_p; /* ptr to addr of the last cycle */
1653  WFG_STACK *bottom_p = NULL; /* bottom of WFG stack */
1654  WFG_STACK *top_p; /* top of WFG stack */
1655  WFG_STACK *stack_elem_p; /* pointer for stack scan */
1656  WFG_CYCLE *cycle_p = NULL; /* pointer to the current cycle */
1657  WFG_WAITER *waiter_p; /* Waiter transactions in the cycle */
1658  int htran_index; /* index of the waiter node of the top edge */
1659  int cycle_group_no; /* cycle group number */
1660  int num_cycles_in_group;
1661  int num_total_cycles = 0;
1662  int error_code = NO_ERROR;
1663 
1664  *cycle_case_p = WFG_CYCLE_NO;
1665  *list_cycles_p = NULL;
1666  last_cycle_p = list_cycles_p;
1667 
1668  if (csect_enter (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
1669  {
1670  return ER_FAILED;
1671  }
1672 
1673  if (wfg_Total_waiters < 2)
1674  {
1675  error_code = ER_FAILED;
1676  goto error;
1677  }
1678 
1679  for (i = 0; i < wfg_Total_nodes; i++)
1680  {
1681  wfg_Nodes[i].status = WFG_NOT_VISITED;
1682  wfg_Nodes[i].cycle_group_no = -1;
1683  }
1684 
1685  /* allocate stack for DFS search */
1686  bottom_p = (WFG_STACK *) malloc (sizeof (WFG_STACK) * wfg_Total_waiters);
1687  if (bottom_p == NULL)
1688  {
1689  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (WFG_STACK) * wfg_Total_waiters);
1690  error_code = ER_OUT_OF_VIRTUAL_MEMORY;
1691  goto error;
1692  }
1693  top_p = bottom_p - 1;
1694  cycle_group_no = 0;
1695 
1696  for (i = 0; i < wfg_Total_nodes; i++)
1697  {
1698  if (max_cycles > 0 && num_total_cycles > max_cycles)
1699  {
1700  /*
1701  * We have too many cycles already. It is better to return to avoid
1702  * spending too much time and space among cycle groups
1703  */
1704  *cycle_case_p = WFG_CYCLE_YES_PRUNE;
1705  break;
1706  }
1707 
1708  if (wfg_Nodes[i].status != WFG_NOT_VISITED)
1709  {
1710  continue;
1711  }
1712 
1713  /* DFS beginning with the current node */
1714  cycle_group_no++;
1715  num_cycles_in_group = 0;
1716 
1717  /*
1718  * Optimization of special case. Used to avoid overhead of stack operations
1719  */
1720  if (wfg_Nodes[i].first_holder_edge_p == NULL)
1721  {
1722  wfg_Nodes[i].status = WFG_OFF_STACK;
1723  continue;
1724  }
1725 
1726  /* start depth-first-search from this node */
1727  wfg_Nodes[i].status = WFG_ON_STACK;
1728  wfg_push_stack (&top_p, i);
1729 
1730  while (top_p >= bottom_p)
1731  {
1732  /* not empty stack */
1733 
1734  new_top: /* new top entry is pushed in stack */
1735 
1736  for (; top_p->current_holder_edge_p != NULL;
1737  top_p->current_holder_edge_p = top_p->current_holder_edge_p->next_holder_edge_p)
1738  {
1739  htran_index = top_p->current_holder_edge_p->holder_tran_index;
1740 
1741  switch (wfg_Nodes[htran_index].status)
1742  {
1743  case WFG_NOT_VISITED:
1744  /* untouched node */
1745  if (wfg_Nodes[htran_index].first_holder_edge_p == NULL)
1746  {
1747  wfg_Nodes[htran_index].status = WFG_OFF_STACK;
1748  }
1749  else
1750  {
1751  /* try the new node */
1752  wfg_Nodes[htran_index].status = WFG_ON_STACK;
1753  wfg_push_stack (&top_p, htran_index);
1754  goto new_top;
1755  }
1756  break;
1757 
1758  case WFG_ON_STACK:
1759  /* a cyclye has been found */
1760 
1761  /* mark this cycle with cycle_group_no */
1762  wfg_Nodes[htran_index].cycle_group_no = cycle_group_no;
1763 
1764  for (stack_elem_p = top_p; stack_elem_p->wait_tran_index != htran_index; stack_elem_p--)
1765  {
1766  wfg_Nodes[stack_elem_p->wait_tran_index].cycle_group_no = cycle_group_no;
1767  }
1768 
1769  /* construct a cycle */
1770  cycle_p = (WFG_CYCLE *) malloc (sizeof (WFG_CYCLE));
1771  if (cycle_p == NULL)
1772  {
1774  error_code = ER_OUT_OF_VIRTUAL_MEMORY;
1775  goto error;
1776  }
1777 
1778  cycle_p->num_trans = (int) ((top_p - stack_elem_p) + 1);
1779  cycle_p->next = NULL;
1780  cycle_p->waiters = (WFG_WAITER *) malloc (sizeof (WFG_WAITER) * cycle_p->num_trans);
1781  if (cycle_p->waiters == NULL)
1782  {
1784  sizeof (WFG_WAITER) * cycle_p->num_trans);
1785  error_code = ER_OUT_OF_VIRTUAL_MEMORY;
1786  free_and_init (cycle_p);
1787  goto error;
1788  }
1789 
1790  j = 0;
1791  for (stack_elem_p = top_p, waiter_p = cycle_p->waiters; j < cycle_p->num_trans;
1792  j++, stack_elem_p--, waiter_p++)
1793  {
1794  waiter_p->tran_index = stack_elem_p->wait_tran_index;
1795  waiter_p->cycle_fun = wfg_Nodes[waiter_p->tran_index].cycle_fun;
1796  waiter_p->args = wfg_Nodes[waiter_p->tran_index].args;
1797  }
1798 
1799  *last_cycle_p = cycle_p;
1800  last_cycle_p = &cycle_p->next;
1801 
1802  num_cycles_in_group++;
1803 
1804  if (max_cycles > 0 && num_total_cycles + num_cycles_in_group >= max_cycles)
1805  {
1806  *cycle_case_p = WFG_CYCLE_YES_PRUNE;
1807  }
1808  else if (*cycle_case_p == WFG_CYCLE_NO)
1809  {
1810  *cycle_case_p = WFG_CYCLE_YES;
1811  }
1812 
1813  break;
1814 
1815  case WFG_OFF_STACK:
1816  /* already traversed */
1817  if (wfg_Nodes[htran_index].cycle_group_no == cycle_group_no)
1818  {
1819  /*
1820  * need to traverse again
1821  *
1822  * We will skip on finding more cycles for
1823  * the current cycle group when we have found many cycles.
1824  * This is done to avoid finding many many combinations of
1825  * cycles for the same transactions.
1826  */
1827 
1828  if ((max_cycles_in_group > 0
1829  && (num_cycles_in_group > wfg_Total_nodes || num_cycles_in_group >= max_cycles_in_group))
1830  || (max_cycles > 0 && (num_total_cycles + num_cycles_in_group) >= max_cycles))
1831  {
1832  *cycle_case_p = WFG_CYCLE_YES_PRUNE;
1833  break;
1834  }
1835 
1836  wfg_Nodes[htran_index].status = WFG_RE_ON_STACK;
1837  wfg_push_stack (&top_p, htran_index);
1838  goto new_top;
1839  }
1840  break;
1841 
1842  case WFG_RE_ON_STACK:
1843  /* this cycle has already been detected */
1844  break;
1845 
1846  default:
1847 #if defined(WFG_DEBUG)
1848  (void) fprintf (stdout, "wfg_detect_ordinary_cycle():");
1849  (void) fprintf (stdout, "interal switch error\n");
1850 #endif /* WFG_DEBUG */
1851  break;
1852  }
1853  }
1854 
1855  /* top_p is searched exhaustedly */
1856  wfg_Nodes[top_p->wait_tran_index].status = WFG_OFF_STACK;
1857  error_code = wfg_pop_stack (&top_p, &bottom_p);
1858  if (error_code != NO_ERROR)
1859  {
1860  goto error;
1861  }
1862  }
1863 
1864  /* empty stack: continue to next cycle group */
1865  num_total_cycles += num_cycles_in_group;
1866  }
1867 
1868  free_and_init (bottom_p); /* free stack */
1869 
1870  csect_exit (thread_p, CSECT_WFG);
1871  return error_code;
1872 
1873 error:
1874  if (*list_cycles_p != NULL)
1875  {
1876  wfg_free_cycle (*list_cycles_p);
1877  *list_cycles_p = NULL;
1878  }
1879 
1880  if (bottom_p != NULL)
1881  {
1882  free_and_init (bottom_p);
1883  }
1884  csect_exit (thread_p, CSECT_WFG);
1885 
1886  return error_code;
1887 }
1888 
1889 /*
1890  * wfg_add_waiters_of_tg :
1891  *
1892  * returns : NO_ERROR
1893  *
1894  * smallest_onstack(in/out):
1895  * holder_node(in):
1896  * tg_index(in):
1897  *
1898  * Note:
1899  */
1900 static int
1901 wfg_add_waiters_of_tg (int *smallest_onstack, int holder_node, int tg_index)
1902 {
1903  WFG_TRANS_LIST *h_tran_list_p; /* ptr to a trans list node in TG */
1904  WFG_TRANS_LIST *w_tran_list_p; /* ptr to a trans list node in TG */
1905  WFG_TRAN_GROUP *tg_tmp;
1906  WFG_NODE *w_node_p;
1907 
1908  /*
1909  * If the node i is a holder of any TG,
1910  * add the waiters of such TG to it.
1911  */
1912  for (; tg_index < wfg_Total_tran_groups; tg_index++)
1913  {
1914  tg_tmp = &wfg_Tran_group[tg_index];
1915  for (h_tran_list_p = tg_tmp->holder_tran_list_p; h_tran_list_p != NULL; h_tran_list_p = h_tran_list_p->next)
1916  {
1917  if (h_tran_list_p->tran_index == holder_node)
1918  {
1919  break;
1920  }
1921  }
1922 
1923  if (h_tran_list_p != NULL)
1924  {
1925  /* Add all waiters of the TG */
1926  for (w_tran_list_p = tg_tmp->waiter_tran_list_p; w_tran_list_p != NULL; w_tran_list_p = w_tran_list_p->next)
1927  {
1928  w_node_p = &wfg_Nodes[w_tran_list_p->tran_index];
1929  if (w_node_p->status == WFG_NOT_VISITED)
1930  {
1931  w_node_p->status = WFG_ON_STACK;
1932  if (*smallest_onstack > w_tran_list_p->tran_index)
1933  {
1934  *smallest_onstack = w_tran_list_p->tran_index;
1935  }
1936  }
1937  }
1938  }
1939  }
1940 
1941  return NO_ERROR;
1942 }
1943 
1944 /*
1945  * wfg_add_waiters_normal_wfg :
1946  *
1947  * returns : NO_ERROR
1948  *
1949  * smallest_onstack(in/out):
1950  * node_index(in):
1951  *
1952  * Note:
1953  */
1954 static int
1955 wfg_add_waiters_normal_wfg (int *smallest_onstack, int node_index)
1956 {
1957  WFG_EDGE *edge_p; /* loop pointer to edge */
1958 
1959  /* Add the waiters of the normal WFG */
1960  for (edge_p = wfg_Nodes[node_index].first_waiter_edge_p; edge_p != NULL; edge_p = edge_p->next_waiter_edge_p)
1961  {
1962  if (wfg_Nodes[edge_p->waiter_tran_index].status == WFG_NOT_VISITED)
1963  {
1964  wfg_Nodes[edge_p->waiter_tran_index].status = WFG_ON_STACK;
1965  if (*smallest_onstack > edge_p->waiter_tran_index)
1966  {
1967  *smallest_onstack = edge_p->waiter_tran_index;
1968  }
1969  }
1970  }
1971 
1972  return NO_ERROR;
1973 }
1974 
1975 /*
1976  * wfg_get_all_waiting_and_add_waiter :
1977  *
1978  * returns : NO_ERROR
1979  *
1980  * all_waiting(out):
1981  * add_waiter(out):
1982  * tg1_index(in):
1983  * tg2_index(in):
1984  *
1985  * Note:
1986  */
1987 static int
1988 wfg_get_all_waiting_and_add_waiter (bool * all_waiting, bool * add_waiter, int tg_index)
1989 {
1990  WFG_TRANS_LIST *w_tran_list_p; /* ptr to a trans list node in TG */
1991  WFG_TRANS_LIST *h_tran_list_p; /* ptr to a trans list node in TG */
1992  WFG_TRAN_GROUP *tg_tmp;
1993  WFG_NODE *w_node_p;
1994  bool tg_connected, tg_all_waiting;
1995  int i;
1996 
1997  /*
1998  * If all holders of connected transaction groups are waiting, then
1999  * we have a deadlock related to TGs. All TG holders will be part
2000  * of TG elementary cycles.
2001  */
2002 
2003  *all_waiting = true;
2004  *add_waiter = true;
2005 
2006  for (i = tg_index; i < wfg_Total_tran_groups && *all_waiting == true; i++)
2007  {
2008  tg_tmp = &wfg_Tran_group[i];
2009  if (i == tg_index)
2010  {
2011  tg_connected = true;
2012  }
2013  else
2014  {
2015  tg_connected = false;
2016  }
2017  for (w_tran_list_p = tg_tmp->waiter_tran_list_p; w_tran_list_p != NULL && tg_connected == false;
2018  w_tran_list_p = w_tran_list_p->next)
2019  {
2020  w_node_p = &wfg_Nodes[w_tran_list_p->tran_index];
2021  if (w_node_p->status != WFG_NOT_VISITED)
2022  {
2023  tg_connected = true;
2024  }
2025  }
2026 
2027  if (tg_connected == false)
2028  {
2029  continue;
2030  }
2031 
2032  tg_all_waiting = true;
2033  for (h_tran_list_p = tg_tmp->holder_tran_list_p; h_tran_list_p != NULL && tg_all_waiting == true;
2034  h_tran_list_p = h_tran_list_p->next)
2035  {
2036  if (wfg_Nodes[h_tran_list_p->tran_index].status != WFG_OFF_STACK)
2037  {
2038  tg_all_waiting = false;
2039  }
2040  else if (w_tran_list_p != NULL && w_tran_list_p->tran_index == h_tran_list_p->tran_index)
2041  {
2042  /*
2043  * The waiter is also a holder. Don't need to add
2044  * the waiter at a later point.
2045  */
2046  *add_waiter = false;
2047  }
2048  }
2049  if (tg_connected == true && tg_all_waiting == false)
2050  {
2051  *all_waiting = false;
2052  }
2053  }
2054 
2055  return NO_ERROR;
2056 }
2057 
2058 /*
2059  * wfg_get_all_waiting_and_add_waiter :
2060  *
2061  * returns : NO_ERROR
2062  *
2063  * all_waiting(out):
2064  * add_waiter(out):
2065  * tg1_index(in):
2066  * tg2_index(in):
2067  *
2068  */
2069 static WFG_CYCLE *
2070 wfg_detect_tran_group_cycle_internal (WFG_CYCLE_CASE * cycle_case_p, WFG_TRANS_LIST * w_tran_list_p, bool add_waiter,
2071  int tg_index, int num_tran_groups_holders)
2072 {
2073  WFG_WAITER *waiter_p; /* Waiter transactions in the cycle */
2074  WFG_TRANS_LIST *h_tran_list_p; /* ptr to a trans list node in TG */
2075  WFG_CYCLE *cycle_p; /* pointer to the current cycle */
2076  int i;
2077 
2078  /*
2079  * Construct the cycle all TG waiters that are part of TG cycles.
2080  */
2081 
2082  if (*cycle_case_p == WFG_CYCLE_NO)
2083  {
2084  *cycle_case_p = WFG_CYCLE_YES;
2085  }
2086 
2087  cycle_p = (WFG_CYCLE *) malloc (sizeof (WFG_CYCLE));
2088  if (cycle_p == NULL)
2089  {
2091  return NULL;
2092  }
2093 
2094  /* Guess the max for now. We will fix it at the end */
2095  cycle_p->num_trans = num_tran_groups_holders;
2096 
2097  if (add_waiter == true)
2098  {
2099  cycle_p->num_trans++;
2100  }
2101 
2102  cycle_p->next = NULL;
2103  cycle_p->waiters = (WFG_WAITER *) malloc (sizeof (WFG_WAITER) * cycle_p->num_trans);
2104  if (cycle_p->waiters == NULL)
2105  {
2106  er_set (ER_ERROR_SEVERITY, ARG_FILE_LINE, ER_OUT_OF_VIRTUAL_MEMORY, 1, sizeof (WFG_WAITER) * cycle_p->num_trans);
2107  free_and_init (cycle_p);
2108  return NULL;
2109  }
2110 
2111  /* Add all TG holders that were part of the connected graph. */
2112  cycle_p->num_trans = 0;
2113  waiter_p = cycle_p->waiters;
2114 
2115  for (i = tg_index; i < wfg_Total_tran_groups; i++)
2116  {
2117  for (h_tran_list_p = wfg_Tran_group[i].holder_tran_list_p; h_tran_list_p != NULL;
2118  h_tran_list_p = h_tran_list_p->next)
2119  {
2120  if (wfg_Nodes[h_tran_list_p->tran_index].status == WFG_OFF_STACK)
2121  {
2122  waiter_p->tran_index = h_tran_list_p->tran_index;
2123  waiter_p->cycle_fun = wfg_Nodes[waiter_p->tran_index].cycle_fun;
2124  waiter_p->args = wfg_Nodes[waiter_p->tran_index].args;
2125  cycle_p->num_trans++;
2126  /*
2127  * Avoid a possible duplication,
2128  * it could be part of holder of another TG.
2129  */
2130  wfg_Nodes[h_tran_list_p->tran_index].status = WFG_ON_TG_CYCLE;
2131  waiter_p++;
2132  }
2133  }
2134  }
2135 
2136  /*
2137  * Add the TG waiter to the cycle. Make sure that the waiter is not
2138  * also a holder. That is, don't duplicate entries.
2139  */
2140  if (add_waiter == true)
2141  {
2142  if (wfg_Nodes[w_tran_list_p->tran_index].status == WFG_OFF_STACK)
2143  {
2144  wfg_Nodes[w_tran_list_p->tran_index].status = WFG_ON_TG_CYCLE;
2145  waiter_p->tran_index = w_tran_list_p->tran_index;
2146  waiter_p->cycle_fun = wfg_Nodes[waiter_p->tran_index].cycle_fun;
2147  waiter_p->args = wfg_Nodes[waiter_p->tran_index].args;
2148  }
2149  }
2150 
2151  return cycle_p;
2152 }
2153 
2154 /*
2155  * wfg_detect_tg_cycle : finds all elementary cycles related with
2156  * Transaction Groups.
2157  *
2158  * returns : NO_ERROR if all OK, ER status otherwise
2159  *
2160  * cycle_case(OUT) : cycle_case. One of the following values:
2161  * WFG_CYCLE_YES_PRUNE
2162  * WFG_CYCLE_YES
2163  * WFG_CYCLE_NO
2164  * WFG_CYCLE_ERROR
2165  * list_cycles_p(IN/OUT) : address to list of cycles
2166  * list_cycles is set as a side effect to point
2167  * to list of cycles.
2168  *
2169  * NOTE: the caller should be responsible for freeing memory allocated
2170  * to the cycles after usage. See wfg_free_cycle()
2171  *
2172  * LIMITATIONS:
2173  * Current implementation does not return all transactions for each
2174  * elementary cycles. Instead, just return holders of TG in cycles.
2175  */
2176 static int
2177 wfg_detect_tran_group_cycle (THREAD_ENTRY * thread_p, WFG_CYCLE_CASE * cycle_case_p, WFG_CYCLE ** list_cycles_p)
2178 {
2179  int smallest_onstack;
2180  int tg1_index, i;
2181  WFG_CYCLE **last_cycle_p; /* ptr to addr of the last cycle */
2182  WFG_TRANS_LIST *w_tran_list_p; /* ptr to a trans list node in TG */
2183  bool all_waiting; /* true if all holders are waiting */
2184  WFG_CYCLE *cycle_p; /* pointer to the current cycle */
2185  bool add_waiter;
2186  int num_tran_groups_holders = 0; /* Add all holders for all tran groups */
2187  int error_code = NO_ERROR;
2188 
2189  *cycle_case_p = WFG_CYCLE_NO;
2190 
2191  *list_cycles_p = NULL;
2192  last_cycle_p = list_cycles_p;
2193 
2194  if (wfg_Total_tran_groups < 1)
2195  {
2196  return error_code;
2197  }
2198 
2199  if (csect_enter (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
2200  {
2201  return ER_FAILED;
2202  }
2203 
2204  for (tg1_index = 0; tg1_index < wfg_Total_tran_groups; tg1_index++)
2205  {
2206  num_tran_groups_holders += wfg_Tran_group[tg1_index].num_holders;
2207  if (num_tran_groups_holders > wfg_Total_nodes)
2208  {
2209  num_tran_groups_holders = wfg_Total_nodes;
2210  }
2211  }
2212 
2213  for (i = 0; i < wfg_Total_nodes; i++)
2214  {
2215  wfg_Nodes[i].status = WFG_NOT_VISITED;
2216  }
2217 
2218  /* Go over each transaction group */
2219  for (tg1_index = 0; tg1_index < wfg_Total_tran_groups; tg1_index++)
2220  {
2221  /*
2222  * Optimization of special case. Used to avoid overhead of stack operations
2223  */
2224  if (wfg_Tran_group[tg1_index].holder_tran_list_p == NULL || wfg_Tran_group[tg1_index].waiter_tran_list_p == NULL)
2225  {
2226  continue;
2227  }
2228 
2229  /*
2230  * Mark status of TG waiters as WFG_ON_STACK
2231  */
2232  for (w_tran_list_p = wfg_Tran_group[tg1_index].waiter_tran_list_p; w_tran_list_p != NULL;
2233  w_tran_list_p = w_tran_list_p->next)
2234  {
2235  /*
2236  * Skip if it has already been in another TG cycle.
2237  * Cycle or subcycle has already been listed.
2238  */
2239  if (wfg_Nodes[w_tran_list_p->tran_index].status == WFG_ON_TG_CYCLE)
2240  {
2241  continue;
2242  }
2243 
2244  for (i = 0; i < wfg_Total_nodes; i++)
2245  {
2246  if (wfg_Nodes[i].status != WFG_ON_TG_CYCLE)
2247  {
2248  wfg_Nodes[i].status = WFG_NOT_VISITED;
2249  }
2250  }
2251 
2252  wfg_Nodes[w_tran_list_p->tran_index].status = WFG_ON_STACK;
2253  smallest_onstack = w_tran_list_p->tran_index;
2254 
2255  /* Loop until there is any more new waiters on stack */
2256  while (smallest_onstack < wfg_Total_nodes)
2257  {
2258  i = smallest_onstack;
2259  smallest_onstack = wfg_Total_nodes;
2260  for (; i < wfg_Total_nodes && i < smallest_onstack; i++)
2261  {
2262  if (wfg_Nodes[i].status == WFG_ON_STACK)
2263  {
2264  wfg_add_waiters_of_tg (&smallest_onstack, i, tg1_index);
2265  wfg_add_waiters_normal_wfg (&smallest_onstack, i);
2266 
2267  /* Indicate that the current node is OFF stack */
2268  wfg_Nodes[i].status = WFG_OFF_STACK;
2269  }
2270  }
2271  }
2272 
2273  wfg_get_all_waiting_and_add_waiter (&all_waiting, &add_waiter, tg1_index);
2274 
2275  if (all_waiting == true)
2276  {
2277  cycle_p =
2278  wfg_detect_tran_group_cycle_internal (cycle_case_p, w_tran_list_p, add_waiter, tg1_index,
2279  num_tran_groups_holders);
2280  if (cycle_p == NULL)
2281  {
2282  goto error;
2283  }
2284  *last_cycle_p = cycle_p;
2285  last_cycle_p = &cycle_p->next;
2286  }
2287  }
2288  }
2289 
2290 end:
2291  csect_exit (thread_p, CSECT_WFG);
2292  return error_code;
2293 
2294 error:
2295  if (*list_cycles_p != NULL)
2296  {
2297  wfg_free_cycle (*list_cycles_p);
2298  *list_cycles_p = NULL;
2299  }
2300  goto end;
2301 }
2302 
2303 /*
2304  * wfg_is_waiting: Find if transaction is waiting for a resource either regular
2305  * or Transaction Group resource.
2306  *
2307  * returns : NO_ERROR if all OK, ER status otherwise
2308  *
2309  */
2310 int
2311 wfg_is_waiting (THREAD_ENTRY * thread_p, const int tran_index)
2312 {
2313  int i;
2314  WFG_EDGE *edge_p;
2315  int error_code = NO_ERROR;
2316 
2317  if (csect_enter_as_reader (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
2318  {
2319  return ER_FAILED;
2320  }
2321 
2322  if (wfg_Total_waiters > 0)
2323  {
2324  for (i = 0; i < wfg_Total_nodes; i++)
2325  {
2326  for (edge_p = wfg_Nodes[i].first_waiter_edge_p; edge_p != NULL; edge_p = edge_p->next_waiter_edge_p)
2327  {
2328  if (tran_index == edge_p->waiter_tran_index)
2329  {
2330  goto end;
2331  }
2332  }
2333  }
2334  }
2335 
2336  error_code = wfg_is_tran_group_waiting (thread_p, tran_index);
2337 
2338 end:
2339  csect_exit (thread_p, CSECT_WFG);
2340 
2341  return error_code;
2342 }
2343 
2344 /*
2345  * wfg_is_tran_group_waiting: Find if transaction is waiting for a TG resource.
2346  *
2347  * returns : NO_ERROR if all OK, ER status otherwise
2348  *
2349  * tran_index(IN): Transaction index
2350  *
2351  */
2352 int
2353 wfg_is_tran_group_waiting (THREAD_ENTRY * thread_p, const int tran_index)
2354 {
2355  int i;
2356  WFG_TRANS_LIST *tran_list_p;
2357  int error_code = ER_FAILED;
2358 
2359  if (csect_enter_as_reader (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
2360  {
2361  return ER_FAILED;
2362  }
2363 
2364  for (i = 0; i < wfg_Total_tran_groups; i++)
2365  {
2366  for (tran_list_p = wfg_Tran_group[i].waiter_tran_list_p; tran_list_p != NULL; tran_list_p = tran_list_p->next)
2367  {
2368  if (tran_index == tran_list_p->tran_index)
2369  {
2370  error_code = NO_ERROR;
2371  goto end;
2372  }
2373  }
2374  }
2375 
2376 end:
2377  csect_exit (thread_p, CSECT_WFG);
2378 
2379  return error_code;
2380 }
2381 
2382 /*
2383  * wfg_get_tran_entries :
2384  *
2385  * return : returns : number of tran entries if all OK, ER status otherwise
2386  *
2387  * tran_index(IN) : Transaction index
2388  *
2389  */
2390 int
2391 wfg_get_tran_entries (THREAD_ENTRY * thread_p, const int tran_index)
2392 {
2393  int i, n = 0;
2394 
2395  if (csect_enter_as_reader (thread_p, CSECT_WFG, INF_WAIT) != NO_ERROR)
2396  {
2397  return ER_FAILED;
2398  }
2399 
2400  for (i = 0; i < wfg_Total_nodes; i++)
2401  {
2402  WFG_EDGE *e;
2403 
2404  for (e = wfg_Nodes[i].first_holder_edge_p; e; e = e->next_holder_edge_p)
2405  {
2406  n += (tran_index == e->waiter_tran_index);
2407  }
2408  for (e = wfg_Nodes[i].first_waiter_edge_p; e; e = e->next_waiter_edge_p)
2409  {
2410  n += (tran_index == e->waiter_tran_index);
2411  }
2412  }
2413 
2414  for (i = 0; i < wfg_Total_tran_groups; i++)
2415  {
2416  WFG_TRANS_LIST *tl;
2417 
2418  for (tl = wfg_Tran_group[i].holder_tran_list_p; tl; tl = tl->next)
2419  {
2420  n += (tran_index == tl->tran_index);
2421  }
2422  for (tl = wfg_Tran_group[i].waiter_tran_list_p; tl; tl = tl->next)
2423  {
2424  n += (tran_index == tl->tran_index);
2425  }
2426  }
2427 
2428  csect_exit (thread_p, CSECT_WFG);
2429 
2430  return n;
2431 }
2432 #endif /* ENABLE_UNUSED_FUNCTION */
#define NO_ERROR
Definition: error_code.h:46
#define ER_FAILED
Definition: error_code.h:47
#define csect_enter(a, b, c)
Definition: cnv.c:138
#define er_log_debug(...)
void THREAD_ENTRY
void er_set(int severity, const char *file_name, const int line_no, int err_id, int num_args,...)
#define assert(x)
#define ER_OUT_OF_VIRTUAL_MEMORY
Definition: error_code.h:50
#define NULL
Definition: freelistheap.h:34
#define csect_exit(a, b)
Definition: cnv.c:139
static void error(const char *msg)
Definition: gencat.c:331
#define ARG_FILE_LINE
Definition: error_manager.h:44
#define csect_enter_as_reader(a, b, c)
#define free_and_init(ptr)
Definition: memory_alloc.h:147
int i
Definition: dynamic_load.c:954