blob: 568bf4d3039bdc49b49e935aa0e11f2cb2ffe505 [file] [log] [blame]
sewardjaf44c822007-11-25 14:01:38 +00001/*
2 This file is part of drd, a data race detector.
3
sewardj85642922008-01-14 11:54:56 +00004 Copyright (C) 2006-2008 Bart Van Assche
sewardjaf44c822007-11-25 14:01:38 +00005 bart.vanassche@gmail.com
6
7 This program is free software; you can redistribute it and/or
8 modify it under the terms of the GNU General Public License as
9 published by the Free Software Foundation; either version 2 of the
10 License, or (at your option) any later version.
11
12 This program is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA
20 02111-1307, USA.
21
22 The GNU General Public License is contained in the file COPYING.
23*/
24
25
bart4bb53d82008-02-28 19:06:34 +000026#include "drd_clientobj.h"
sewardjaf44c822007-11-25 14:01:38 +000027#include "drd_error.h"
28#include "drd_mutex.h"
sewardj721ad7b2007-11-30 08:30:29 +000029#include "priv_drd_clientreq.h"
sewardjaf44c822007-11-25 14:01:38 +000030#include "pub_tool_errormgr.h" // VG_(maybe_record_error)()
31#include "pub_tool_libcassert.h" // tl_assert()
bart4bb53d82008-02-28 19:06:34 +000032#include "pub_tool_libcprint.h" // VG_(message)()
sewardjaf44c822007-11-25 14:01:38 +000033#include "pub_tool_machine.h" // VG_(get_IP)()
34#include "pub_tool_threadstate.h" // VG_(get_running_tid)()
35
36
sewardj347eeba2008-01-21 14:19:07 +000037// Local functions.
38
bart46d5f172008-02-28 19:49:37 +000039static void mutex_cleanup(struct mutex_info* p);
bart5357fcb2008-02-27 15:46:00 +000040static Bool mutex_is_locked(struct mutex_info* const p);
sewardj347eeba2008-01-21 14:19:07 +000041static void mutex_destroy(struct mutex_info* const p);
42
43
sewardjaf44c822007-11-25 14:01:38 +000044// Local variables.
45
46static Bool s_trace_mutex;
47static ULong s_mutex_lock_count;
sewardjaf44c822007-11-25 14:01:38 +000048
49
50// Function definitions.
51
52void mutex_set_trace(const Bool trace_mutex)
53{
54 tl_assert(!! trace_mutex == trace_mutex);
55 s_trace_mutex = trace_mutex;
56}
57
58static
59void mutex_initialize(struct mutex_info* const p,
60 const Addr mutex,
sewardj721ad7b2007-11-30 08:30:29 +000061 const SizeT size,
62 const MutexT mutex_type)
sewardjaf44c822007-11-25 14:01:38 +000063{
64 tl_assert(mutex != 0);
65 tl_assert(size > 0);
66
bart4bb53d82008-02-28 19:06:34 +000067 tl_assert(p->a1 == mutex);
68 tl_assert(p->a2 == mutex + size);
bart46d5f172008-02-28 19:49:37 +000069 p->cleanup = (void(*)(DrdClientobj*))&mutex_cleanup;
sewardj721ad7b2007-11-30 08:30:29 +000070 p->mutex_type = mutex_type;
sewardjaf44c822007-11-25 14:01:38 +000071 p->recursion_count = 0;
72 p->owner = DRD_INVALID_THREADID;
73 vc_init(&p->vc, 0, 0);
74}
75
bart46d5f172008-02-28 19:49:37 +000076/** Deallocate the memory that was allocated by mutex_initialize(). */
77static void mutex_cleanup(struct mutex_info* p)
78{
bartb78312c2008-02-29 11:00:17 +000079 if (s_trace_mutex)
80 {
bart3b1ee452008-02-29 19:28:15 +000081 VG_(message)(Vg_UserMsg,
82 "[%d/%d] mutex_destroy %s 0x%lx",
83 VG_(get_running_tid)(),
84 thread_get_running_tid(),
bartb78312c2008-02-29 11:00:17 +000085 mutex_get_typename(p),
86 p->a1);
87 }
88
bart46d5f172008-02-28 19:49:37 +000089 if (mutex_is_locked(p))
90 {
91 MutexErrInfo MEI = { p->a1, p->recursion_count, p->owner };
92 VG_(maybe_record_error)(VG_(get_running_tid)(),
93 MutexErr,
94 VG_(get_IP)(VG_(get_running_tid)()),
95 "Destroying locked mutex",
96 &MEI);
97 }
98
99 vc_cleanup(&p->vc);
100}
101
sewardjaf44c822007-11-25 14:01:38 +0000102static
sewardj721ad7b2007-11-30 08:30:29 +0000103struct mutex_info*
104mutex_get_or_allocate(const Addr mutex,
105 const SizeT size,
106 const MutexT mutex_type)
sewardjaf44c822007-11-25 14:01:38 +0000107{
bart4bb53d82008-02-28 19:06:34 +0000108 struct mutex_info* p;
sewardj721ad7b2007-11-30 08:30:29 +0000109
bart4bb53d82008-02-28 19:06:34 +0000110 tl_assert(offsetof(DrdClientobj, mutex) == 0);
111 p = &drd_clientobj_get(mutex, ClientMutex)->mutex;
112 if (p)
113 {
114 tl_assert(p->mutex_type == mutex_type);
115 tl_assert(p->a2 - p->a1 == size);
116 return p;
117 }
sewardj721ad7b2007-11-30 08:30:29 +0000118
bart4bb53d82008-02-28 19:06:34 +0000119 if (drd_clientobj_present(mutex, mutex + size))
sewardj721ad7b2007-11-30 08:30:29 +0000120 {
bart4bb53d82008-02-28 19:06:34 +0000121 GenericErrInfo GEI;
122 VG_(maybe_record_error)(VG_(get_running_tid)(),
123 GenericErr,
124 VG_(get_IP)(VG_(get_running_tid)()),
125 "Not a mutex",
126 &GEI);
127 return 0;
sewardj721ad7b2007-11-30 08:30:29 +0000128 }
bart4bb53d82008-02-28 19:06:34 +0000129
130 p = &drd_clientobj_add(mutex, mutex + size, ClientMutex)->mutex;
131 mutex_initialize(p, mutex, size, mutex_type);
132 return p;
sewardjaf44c822007-11-25 14:01:38 +0000133}
134
bart3b1ee452008-02-29 19:28:15 +0000135struct mutex_info* mutex_get(const Addr mutex)
136{
137 tl_assert(offsetof(DrdClientobj, mutex) == 0);
138 return &drd_clientobj_get(mutex, ClientMutex)->mutex;
139}
140
sewardj721ad7b2007-11-30 08:30:29 +0000141struct mutex_info*
142mutex_init(const Addr mutex, const SizeT size, const MutexT mutex_type)
sewardjaf44c822007-11-25 14:01:38 +0000143{
144 struct mutex_info* mutex_p;
145
sewardjaf44c822007-11-25 14:01:38 +0000146 if (s_trace_mutex)
147 {
bart3b1ee452008-02-29 19:28:15 +0000148 VG_(message)(Vg_UserMsg,
149 "[%d/%d] mutex_init %s 0x%lx",
150 VG_(get_running_tid)(),
151 thread_get_running_tid(),
sewardj347eeba2008-01-21 14:19:07 +0000152 mutex_type_name(mutex_type),
sewardjaf44c822007-11-25 14:01:38 +0000153 mutex);
154 }
155
sewardj347eeba2008-01-21 14:19:07 +0000156 mutex_p = mutex_get(mutex);
157 if (mutex_p)
158 {
159 const ThreadId vg_tid = VG_(get_running_tid)();
160 MutexErrInfo MEI
bart4bb53d82008-02-28 19:06:34 +0000161 = { mutex_p->a1, mutex_p->recursion_count, mutex_p->owner };
sewardj347eeba2008-01-21 14:19:07 +0000162 VG_(maybe_record_error)(vg_tid,
163 MutexErr,
164 VG_(get_IP)(vg_tid),
165 "Mutex reinitialization",
166 &MEI);
bartb78312c2008-02-29 11:00:17 +0000167 return mutex_p;
sewardj347eeba2008-01-21 14:19:07 +0000168 }
169 mutex_p = mutex_get_or_allocate(mutex, size, mutex_type);
170
sewardjaf44c822007-11-25 14:01:38 +0000171 return mutex_p;
172}
173
sewardj347eeba2008-01-21 14:19:07 +0000174static void mutex_destroy(struct mutex_info* const p)
sewardjaf44c822007-11-25 14:01:38 +0000175{
bart28230a32008-02-29 17:27:03 +0000176 drd_clientobj_remove(p->a1, ClientMutex);
sewardjaf44c822007-11-25 14:01:38 +0000177}
178
bart46d5f172008-02-28 19:49:37 +0000179/** Called after pthread_mutex_destroy(). */
sewardj347eeba2008-01-21 14:19:07 +0000180void mutex_post_destroy(const Addr mutex)
181{
182 struct mutex_info* p;
183
184 p = mutex_get(mutex);
185 tl_assert(p);
186 if (p)
187 {
bart46d5f172008-02-28 19:49:37 +0000188 mutex_destroy(p);
sewardj347eeba2008-01-21 14:19:07 +0000189 }
190}
191
bart8bba1f72008-02-27 16:13:05 +0000192/** Called before pthread_mutex_lock() is invoked. If a data structure for
193 * the client-side object was not yet created, do this now. Also check whether
194 * an attempt is made to lock recursively a synchronization object that must
195 * not be locked recursively.
196 */
197void mutex_pre_lock(const Addr mutex, const SizeT size, MutexT mutex_type)
198{
bart635cb162008-02-28 08:30:43 +0000199 struct mutex_info* p;
200
201 p = mutex_get(mutex);
bart8bba1f72008-02-27 16:13:05 +0000202 if (p == 0)
203 {
204 mutex_init(mutex, size, mutex_type);
205 p = mutex_get(mutex);
206 }
bart635cb162008-02-28 08:30:43 +0000207
bart8bba1f72008-02-27 16:13:05 +0000208 tl_assert(p);
209
210 if (p->owner == thread_get_running_tid()
211 && p->recursion_count >= 1
212 && mutex_type != mutex_type_recursive_mutex)
213 {
bart4bb53d82008-02-28 19:06:34 +0000214 MutexErrInfo MEI = { p->a1, p->recursion_count, p->owner };
bart8bba1f72008-02-27 16:13:05 +0000215 VG_(maybe_record_error)(VG_(get_running_tid)(),
216 MutexErr,
217 VG_(get_IP)(VG_(get_running_tid)()),
218 "Recursive locking not allowed",
219 &MEI);
220 }
221}
222
sewardjaf44c822007-11-25 14:01:38 +0000223/**
224 * Update mutex_info state when locking the pthread_mutex_t mutex.
225 * Note: this function must be called after pthread_mutex_lock() has been
226 * called, or a race condition is triggered !
227 */
bart3b1ee452008-02-29 19:28:15 +0000228int mutex_post_lock(const Addr mutex, const SizeT size, MutexT mutex_type,
229 const Bool took_lock)
sewardjaf44c822007-11-25 14:01:38 +0000230{
bart3b1ee452008-02-29 19:28:15 +0000231 const DrdThreadId drd_tid = thread_get_running_tid();
sewardj721ad7b2007-11-30 08:30:29 +0000232 struct mutex_info* const p = mutex_get_or_allocate(mutex, size, mutex_type);
sewardjaf44c822007-11-25 14:01:38 +0000233
234 if (s_trace_mutex)
235 {
bart3b1ee452008-02-29 19:28:15 +0000236 VG_(message)(Vg_UserMsg,
237 "[%d/%d] post_mutex_lock %s 0x%lx rc %d owner %d",
238 VG_(get_running_tid)(),
sewardjaf44c822007-11-25 14:01:38 +0000239 drd_tid,
240 mutex_get_typename(p),
241 mutex,
242 p ? p->recursion_count : 0,
243 p ? p->owner : VG_INVALID_THREADID);
244 }
245
bart635cb162008-02-28 08:30:43 +0000246 if (mutex_type == mutex_type_invalid_mutex)
247 {
248 GenericErrInfo GEI;
249 VG_(maybe_record_error)(VG_(get_running_tid)(),
250 GenericErr,
251 VG_(get_IP)(VG_(get_running_tid)()),
252 "Invalid mutex",
253 &GEI);
254 }
255
bartab7a6442008-02-25 19:46:14 +0000256 if (p == 0)
257 {
barte883bc82008-02-26 19:13:04 +0000258 GenericErrInfo GEI;
bartab7a6442008-02-25 19:46:14 +0000259 VG_(maybe_record_error)(VG_(get_running_tid)(),
barte883bc82008-02-26 19:13:04 +0000260 GenericErr,
bartab7a6442008-02-25 19:46:14 +0000261 VG_(get_IP)(VG_(get_running_tid)()),
262 "Not a mutex",
barte883bc82008-02-26 19:13:04 +0000263 &GEI);
bartab7a6442008-02-25 19:46:14 +0000264 return 0;
265 }
266
bart3b1ee452008-02-29 19:28:15 +0000267 if (! took_lock)
268 return p->recursion_count;
bart5357fcb2008-02-27 15:46:00 +0000269
sewardj721ad7b2007-11-30 08:30:29 +0000270 tl_assert(p->mutex_type == mutex_type);
bart4bb53d82008-02-28 19:06:34 +0000271 tl_assert(p->a2 - p->a1 == size);
sewardj721ad7b2007-11-30 08:30:29 +0000272
sewardjaf44c822007-11-25 14:01:38 +0000273 if (p->recursion_count == 0)
274 {
275 p->owner = drd_tid;
276 s_mutex_lock_count++;
277 }
278 else if (p->owner != drd_tid)
279 {
bart3b1ee452008-02-29 19:28:15 +0000280 VG_(message)(Vg_UserMsg,
sewardjaf44c822007-11-25 14:01:38 +0000281 "The impossible happened: mutex 0x%lx is locked"
282 " simultaneously by two threads (recursion count %d,"
283 " owners %d and %d) !",
bart4bb53d82008-02-28 19:06:34 +0000284 p->a1, p->recursion_count, p->owner, drd_tid);
sewardj347eeba2008-01-21 14:19:07 +0000285 p->owner = drd_tid;
sewardjaf44c822007-11-25 14:01:38 +0000286 }
287 p->recursion_count++;
288
289 if (p->recursion_count == 1)
290 {
bartab7a6442008-02-25 19:46:14 +0000291 const DrdThreadId last_owner = p->owner;
292
sewardjaf44c822007-11-25 14:01:38 +0000293 if (last_owner != drd_tid && last_owner != DRD_INVALID_THREADID)
294 thread_combine_vc2(drd_tid, mutex_get_last_vc(mutex));
295 thread_new_segment(drd_tid);
296 }
297
298 return p->recursion_count;
299}
300
301/**
302 * Update mutex_info state when unlocking the pthread_mutex_t mutex.
303 * Note: this function must be called before pthread_mutex_unlock() is called,
304 * or a race condition is triggered !
bartb78312c2008-02-29 11:00:17 +0000305 * @return New value of the mutex recursion count.
sewardjaf44c822007-11-25 14:01:38 +0000306 * @param mutex Pointer to pthread_mutex_t data structure in the client space.
307 * @param tid ThreadId of the thread calling pthread_mutex_unlock().
308 * @param vc Pointer to the current vector clock of thread tid.
309 */
sewardj721ad7b2007-11-30 08:30:29 +0000310int mutex_unlock(const Addr mutex, const MutexT mutex_type)
sewardjaf44c822007-11-25 14:01:38 +0000311{
bartb78312c2008-02-29 11:00:17 +0000312 const DrdThreadId drd_tid = thread_get_running_tid();
313 const ThreadId vg_tid = VG_(get_running_tid)();
sewardjaf44c822007-11-25 14:01:38 +0000314 const VectorClock* const vc = thread_get_vc(drd_tid);
315 struct mutex_info* const p = mutex_get(mutex);
316
bartb78312c2008-02-29 11:00:17 +0000317 if (s_trace_mutex && p != 0)
sewardjaf44c822007-11-25 14:01:38 +0000318 {
bart3b1ee452008-02-29 19:28:15 +0000319 VG_(message)(Vg_UserMsg,
320 "[%d/%d] mutex_unlock %s 0x%lx rc %d",
321 vg_tid,
322 drd_tid,
sewardjaf44c822007-11-25 14:01:38 +0000323 mutex_get_typename(p),
324 mutex,
325 p->recursion_count,
326 p->owner);
327 }
328
bart635cb162008-02-28 08:30:43 +0000329 if (mutex_type == mutex_type_invalid_mutex)
330 {
331 GenericErrInfo GEI;
332 VG_(maybe_record_error)(VG_(get_running_tid)(),
333 GenericErr,
334 VG_(get_IP)(VG_(get_running_tid)()),
335 "Invalid mutex",
336 &GEI);
337 }
338
bart5357fcb2008-02-27 15:46:00 +0000339 if (p == 0)
bartab7a6442008-02-25 19:46:14 +0000340 {
barte883bc82008-02-26 19:13:04 +0000341 GenericErrInfo GEI;
bartab7a6442008-02-25 19:46:14 +0000342 VG_(maybe_record_error)(vg_tid,
barte883bc82008-02-26 19:13:04 +0000343 GenericErr,
bartab7a6442008-02-25 19:46:14 +0000344 VG_(get_IP)(vg_tid),
345 "Not a mutex",
barte883bc82008-02-26 19:13:04 +0000346 &GEI);
bartab7a6442008-02-25 19:46:14 +0000347 return 0;
348 }
349
bart5357fcb2008-02-27 15:46:00 +0000350 if (p->owner == DRD_INVALID_THREADID)
351 {
bart4bb53d82008-02-28 19:06:34 +0000352 MutexErrInfo MEI = { p->a1, p->recursion_count, p->owner };
bart5357fcb2008-02-27 15:46:00 +0000353 VG_(maybe_record_error)(vg_tid,
354 MutexErr,
355 VG_(get_IP)(vg_tid),
356 "Mutex not locked",
357 &MEI);
358 return 0;
359 }
360
sewardjaf44c822007-11-25 14:01:38 +0000361 tl_assert(p);
bart5357fcb2008-02-27 15:46:00 +0000362 if (p->mutex_type != mutex_type)
363 {
bart3b1ee452008-02-29 19:28:15 +0000364 VG_(message)(Vg_UserMsg, "??? mutex %p: type changed from %d into %d",
bart4bb53d82008-02-28 19:06:34 +0000365 p->a1, p->mutex_type, mutex_type);
bart5357fcb2008-02-27 15:46:00 +0000366 }
sewardj721ad7b2007-11-30 08:30:29 +0000367 tl_assert(p->mutex_type == mutex_type);
sewardjaf44c822007-11-25 14:01:38 +0000368 tl_assert(p->owner != DRD_INVALID_THREADID);
sewardj721ad7b2007-11-30 08:30:29 +0000369
sewardjaf44c822007-11-25 14:01:38 +0000370 if (p->owner != drd_tid)
371 {
bart4bb53d82008-02-28 19:06:34 +0000372 MutexErrInfo MEI = { p->a1, p->recursion_count, p->owner };
sewardjaf44c822007-11-25 14:01:38 +0000373 VG_(maybe_record_error)(vg_tid,
374 MutexErr,
375 VG_(get_IP)(vg_tid),
376 "Mutex not unlocked by owner thread",
377 &MEI);
378 }
379 p->recursion_count--;
sewardj347eeba2008-01-21 14:19:07 +0000380 if (p->recursion_count < 0)
381 {
382 MutexErrInfo MEI
bart4bb53d82008-02-28 19:06:34 +0000383 = { p->a1, p->recursion_count, p->owner };
sewardj347eeba2008-01-21 14:19:07 +0000384 VG_(maybe_record_error)(vg_tid,
385 MutexErr,
386 VG_(get_IP)(vg_tid),
387 "Attempt to unlock a mutex that is not locked",
388 &MEI);
389 p->recursion_count = 0;
390 }
391
sewardjaf44c822007-11-25 14:01:38 +0000392 if (p->recursion_count == 0)
393 {
394 /* This pthread_mutex_unlock() call really unlocks the mutex. Save the */
395 /* current vector clock of the thread such that it is available when */
396 /* this mutex is locked again. */
bart301c3112008-02-24 18:22:37 +0000397 vc_assign(&p->vc, vc);
sewardjaf44c822007-11-25 14:01:38 +0000398
399 thread_new_segment(drd_tid);
400 }
401 return p->recursion_count;
402}
403
404const char* mutex_get_typename(struct mutex_info* const p)
405{
406 tl_assert(p);
sewardj721ad7b2007-11-30 08:30:29 +0000407
sewardj347eeba2008-01-21 14:19:07 +0000408 return mutex_type_name(p->mutex_type);
409}
410
411const char* mutex_type_name(const MutexT mt)
412{
413 switch (mt)
sewardjaf44c822007-11-25 14:01:38 +0000414 {
bart635cb162008-02-28 08:30:43 +0000415 case mutex_type_invalid_mutex:
416 return "invalid mutex";
bart5357fcb2008-02-27 15:46:00 +0000417 case mutex_type_recursive_mutex:
418 return "recursive mutex";
419 case mutex_type_errorcheck_mutex:
420 return "error checking mutex";
421 case mutex_type_default_mutex:
sewardjaf44c822007-11-25 14:01:38 +0000422 return "mutex";
sewardj721ad7b2007-11-30 08:30:29 +0000423 case mutex_type_spinlock:
sewardjaf44c822007-11-25 14:01:38 +0000424 return "spinlock";
425 default:
426 tl_assert(0);
427 }
428 return "?";
429}
430
bart5357fcb2008-02-27 15:46:00 +0000431/** Return true if the specified mutex is locked by any thread. */
432static Bool mutex_is_locked(struct mutex_info* const p)
433{
434 tl_assert(p);
435 return (p->recursion_count > 0);
436}
437
sewardjaf44c822007-11-25 14:01:38 +0000438Bool mutex_is_locked_by(const Addr mutex, const DrdThreadId tid)
439{
440 struct mutex_info* const p = mutex_get(mutex);
sewardjaf44c822007-11-25 14:01:38 +0000441 if (p)
442 {
443 return (p->recursion_count > 0 && p->owner == tid);
444 }
445 return False;
446}
447
448const VectorClock* mutex_get_last_vc(const Addr mutex)
449{
450 struct mutex_info* const p = mutex_get(mutex);
451 return p ? &p->vc : 0;
452}
453
454int mutex_get_recursion_count(const Addr mutex)
455{
456 struct mutex_info* const p = mutex_get(mutex);
457 tl_assert(p);
458 return p->recursion_count;
459}
460
461/**
bart301c3112008-02-24 18:22:37 +0000462 * Call this function when thread tid stops to exist, such that the
sewardjaf44c822007-11-25 14:01:38 +0000463 * "last owner" field can be cleared if it still refers to that thread.
sewardjaf44c822007-11-25 14:01:38 +0000464 */
bart301c3112008-02-24 18:22:37 +0000465void mutex_thread_delete(const DrdThreadId tid)
sewardjaf44c822007-11-25 14:01:38 +0000466{
bart4bb53d82008-02-28 19:06:34 +0000467 struct mutex_info* p;
468
469 drd_clientobj_resetiter();
470 for ( ; (p = &drd_clientobj_next(ClientMutex)->mutex) != 0; )
sewardjaf44c822007-11-25 14:01:38 +0000471 {
bart4bb53d82008-02-28 19:06:34 +0000472 if (p->owner == tid && p->recursion_count > 0)
sewardjaf44c822007-11-25 14:01:38 +0000473 {
bart5357fcb2008-02-27 15:46:00 +0000474 MutexErrInfo MEI
bart4bb53d82008-02-28 19:06:34 +0000475 = { p->a1, p->recursion_count, p->owner };
bart5357fcb2008-02-27 15:46:00 +0000476 VG_(maybe_record_error)(VG_(get_running_tid)(),
477 MutexErr,
478 VG_(get_IP)(VG_(get_running_tid)()),
479 "Mutex still locked at thread exit",
480 &MEI);
sewardjaf44c822007-11-25 14:01:38 +0000481 p->owner = VG_INVALID_THREADID;
482 }
483 }
484}
485
sewardjaf44c822007-11-25 14:01:38 +0000486ULong get_mutex_lock_count(void)
487{
488 return s_mutex_lock_count;
489}
sewardj347eeba2008-01-21 14:19:07 +0000490
491
492/*
493 * Local variables:
494 * c-basic-offset: 3
495 * End:
496 */