OSDN Git Service

libitm:
[pf3gnuchains/gcc-fork.git] / libitm / retry.cc
1 /* Copyright (C) 2008, 2009, 2011 Free Software Foundation, Inc.
2    Contributed by Richard Henderson <rth@redhat.com>.
3
4    This file is part of the GNU Transactional Memory Library (libitm).
5
6    Libitm is free software; you can redistribute it and/or modify it
7    under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3 of the License, or
9    (at your option) any later version.
10
11    Libitm is distributed in the hope that it will be useful, but WITHOUT ANY
12    WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
13    FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
14    more details.
15
16    Under Section 7 of GPL version 3, you are granted additional
17    permissions described in the GCC Runtime Library Exception, version
18    3.1, as published by the Free Software Foundation.
19
20    You should have received a copy of the GNU General Public License and
21    a copy of the GCC Runtime Library Exception along with this program;
22    see the files COPYING3 and COPYING.RUNTIME respectively.  If not, see
23    <http://www.gnu.org/licenses/>.  */
24
25 #include <stdlib.h>
26 #include <string.h>
27 #include <ctype.h>
28 #include "libitm_i.h"
29
30 // The default TM method used when starting a new transaction.
31 static GTM::abi_dispatch* default_dispatch = 0;
32 // The default TM method as requested by the user, if any.
33 static GTM::abi_dispatch* default_dispatch_user = 0;
34
35 void
36 GTM::gtm_thread::decide_retry_strategy (gtm_restart_reason r)
37 {
38   struct abi_dispatch *disp = abi_disp ();
39
40   this->restart_reason[r]++;
41   this->restart_total++;
42
43   if (r == RESTART_INIT_METHOD_GROUP)
44     {
45       // A re-initializations of the method group has been requested. Switch
46       // to serial mode, initialize, and resume normal operation.
47       if ((state & STATE_SERIAL) == 0)
48         {
49           // We have to eventually re-init the method group. Therefore,
50           // we cannot just upgrade to a write lock here because this could
51           // fail forever when other transactions execute in serial mode.
52           // However, giving up the read lock then means that a change of the
53           // method group could happen in-between, so check that we're not
54           // re-initializing without a need.
55           // ??? Note that we can still re-initialize too often, but avoiding
56           // that would increase code complexity, which seems unnecessary
57           // given that re-inits should be very infrequent.
58           serial_lock.read_unlock(this);
59           serial_lock.write_lock();
60           if (disp->get_method_group() == default_dispatch->get_method_group())
61             {
62               // Still the same method group.
63               disp->get_method_group()->fini();
64               disp->get_method_group()->init();
65             }
66           serial_lock.write_unlock();
67           serial_lock.read_lock(this);
68           if (disp->get_method_group() != default_dispatch->get_method_group())
69             {
70               disp = default_dispatch;
71               set_abi_disp(disp);
72             }
73         }
74       else
75         {
76           // We are a serial transaction already, which makes things simple.
77           disp->get_method_group()->fini();
78           disp->get_method_group()->init();
79         }
80     }
81
82   bool retry_irr = (r == RESTART_SERIAL_IRR);
83   bool retry_serial = (retry_irr || this->restart_total > 100);
84
85   // We assume closed nesting to be infrequently required, so just use
86   // dispatch_serial (with undo logging) if required.
87   if (r == RESTART_CLOSED_NESTING)
88     retry_serial = true;
89
90   if (retry_serial)
91     {
92       // In serialirr_mode we can succeed with the upgrade to
93       // write-lock but fail the trycommit.  In any case, if the
94       // write lock is not yet held, grab it.  Don't do this with
95       // an upgrade, since we've no need to preserve the state we
96       // acquired with the read.
97       // Note that we will be restarting with either dispatch_serial or
98       // dispatch_serialirr, which are compatible with all TM methods; if
99       // we would retry with a different method, we would have to first check
100       // whether the default dispatch or the method group have changed. Also,
101       // the caller must have rolled back the previous transaction, so we
102       // don't have to worry about things such as privatization.
103       if ((this->state & STATE_SERIAL) == 0)
104         {
105           this->state |= STATE_SERIAL;
106           serial_lock.read_unlock (this);
107           serial_lock.write_lock ();
108         }
109
110       // We can retry with dispatch_serialirr if the transaction
111       // doesn't contain an abort and if we don't need closed nesting.
112       if ((this->prop & pr_hasNoAbort) && (r != RESTART_CLOSED_NESTING))
113         retry_irr = true;
114     }
115
116   // Note that we can just use serial mode here without having to switch
117   // TM method sets because serial mode is compatible with all of them.
118   if (retry_irr)
119     {
120       this->state = (STATE_SERIAL | STATE_IRREVOCABLE);
121       disp = dispatch_serialirr ();
122       set_abi_disp (disp);
123     }
124   else if (retry_serial)
125     {
126       disp = dispatch_serial();
127       set_abi_disp (disp);
128     }
129 }
130
131
132 // Decides which TM method should be used on the first attempt to run this
133 // transaction.
134 GTM::abi_dispatch*
135 GTM::gtm_thread::decide_begin_dispatch (uint32_t prop)
136 {
137   // TODO Pay more attention to prop flags (eg, *omitted) when selecting
138   // dispatch.
139   if ((prop & pr_doesGoIrrevocable) || !(prop & pr_instrumentedCode))
140     return dispatch_serialirr();
141
142   // If we might need closed nesting and the default dispatch has an
143   // alternative that supports closed nesting, use it.
144   // ??? We could choose another TM method that we know supports closed
145   // nesting but isn't the default (e.g., dispatch_serial()). However, we
146   // assume that aborts that need closed nesting are infrequent, so don't
147   // choose a non-default method until we have to actually restart the
148   // transaction.
149   if (!(prop & pr_hasNoAbort) && !default_dispatch->closed_nesting()
150       && default_dispatch->closed_nesting_alternative())
151     return default_dispatch->closed_nesting_alternative();
152
153   // No special case, just use the default dispatch.
154   return default_dispatch;
155 }
156
157
158 void
159 GTM::gtm_thread::set_default_dispatch(GTM::abi_dispatch* disp)
160 {
161   if (default_dispatch == disp)
162     return;
163   if (default_dispatch)
164     {
165       // If we are switching method groups, initialize and shut down properly.
166       if (default_dispatch->get_method_group() != disp->get_method_group())
167         {
168           default_dispatch->get_method_group()->fini();
169           disp->get_method_group()->init();
170         }
171     }
172   else
173     disp->get_method_group()->init();
174   default_dispatch = disp;
175 }
176
177
178 static GTM::abi_dispatch*
179 parse_default_method()
180 {
181   const char *env = getenv("ITM_DEFAULT_METHOD");
182   GTM::abi_dispatch* disp = 0;
183   if (env == NULL)
184     return 0;
185
186   while (isspace((unsigned char) *env))
187     ++env;
188   if (strncmp(env, "serialirr_onwrite", 17) == 0)
189     {
190       disp = GTM::dispatch_serialirr_onwrite();
191       env += 17;
192     }
193   else if (strncmp(env, "serialirr", 9) == 0)
194     {
195       disp = GTM::dispatch_serialirr();
196       env += 9;
197     }
198   else if (strncmp(env, "serial", 6) == 0)
199     {
200       disp = GTM::dispatch_serial();
201       env += 6;
202     }
203   else if (strncmp(env, "gl_wt", 5) == 0)
204     {
205       disp = GTM::dispatch_gl_wt();
206       env += 5;
207     }
208   else
209     goto unknown;
210
211   while (isspace((unsigned char) *env))
212     ++env;
213   if (*env == '\0')
214     return disp;
215
216  unknown:
217   GTM::GTM_error("Unknown TM method in environment variable "
218       "ITM_DEFAULT_METHOD\n");
219   return 0;
220 }
221
222 // Gets notifications when the number of registered threads changes. This is
223 // used to initialize the method set choice and trigger straightforward choice
224 // adaption.
225 // This must be called only by serial threads.
226 void
227 GTM::gtm_thread::number_of_threads_changed(unsigned previous, unsigned now)
228 {
229   if (previous == 0)
230     {
231       // No registered threads before, so initialize.
232       static bool initialized = false;
233       if (!initialized)
234         {
235           initialized = true;
236           // Check for user preferences here.
237           default_dispatch_user = parse_default_method();
238         }
239     }
240   else if (now == 0)
241     {
242       // No registered threads anymore. The dispatch based on serial mode do
243       // not have any global state, so this effectively shuts down properly.
244       set_default_dispatch(dispatch_serialirr());
245     }
246
247   if (now == 1)
248     {
249       // Only one thread, so use a serializing method.
250       // ??? If we don't have a fast serial mode implementation, it might be
251       // better to use the global lock method set here.
252       if (default_dispatch_user)
253         set_default_dispatch(default_dispatch_user);
254       else
255         set_default_dispatch(dispatch_serialirr());
256     }
257   else if (now > 1 && previous <= 1)
258     {
259       // More than one thread, use the default method.
260       if (default_dispatch_user)
261         set_default_dispatch(default_dispatch_user);
262       else
263         set_default_dispatch(dispatch_serialirr_onwrite());
264     }
265 }