1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "base/memory/weak_ptr.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/location.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/single_thread_task_runner.h"
13 #include "base/synchronization/waitable_event.h"
14 #include "base/threading/thread.h"
15 #include "testing/gtest/include/gtest/gtest.h"
16
17 namespace base {
18 namespace {
19
20 template <class T>
21 class OffThreadObjectCreator {
22 public:
NewObject()23 static T* NewObject() {
24 T* result;
25 {
26 Thread creator_thread("creator_thread");
27 creator_thread.Start();
28 creator_thread.task_runner()->PostTask(
29 FROM_HERE, base::Bind(OffThreadObjectCreator::CreateObject, &result));
30 }
31 DCHECK(result); // We synchronized on thread destruction above.
32 return result;
33 }
34 private:
CreateObject(T ** result)35 static void CreateObject(T** result) {
36 *result = new T;
37 }
38 };
39
40 struct Base {
41 std::string member;
42 };
43 struct Derived : public Base {};
44
45 struct TargetBase {};
46 struct Target : public TargetBase, public SupportsWeakPtr<Target> {
~Targetbase::__anonba1207970111::Target47 virtual ~Target() {}
48 };
49 struct DerivedTarget : public Target {};
50 struct Arrow {
51 WeakPtr<Target> target;
52 };
53 struct TargetWithFactory : public Target {
TargetWithFactorybase::__anonba1207970111::TargetWithFactory54 TargetWithFactory() : factory(this) {}
55 WeakPtrFactory<Target> factory;
56 };
57
58 // Helper class to create and destroy weak pointer copies
59 // and delete objects on a background thread.
60 class BackgroundThread : public Thread {
61 public:
BackgroundThread()62 BackgroundThread() : Thread("owner_thread") {}
63
~BackgroundThread()64 ~BackgroundThread() override { Stop(); }
65
CreateArrowFromTarget(Arrow ** arrow,Target * target)66 void CreateArrowFromTarget(Arrow** arrow, Target* target) {
67 WaitableEvent completion(true, false);
68 task_runner()->PostTask(
69 FROM_HERE, base::Bind(&BackgroundThread::DoCreateArrowFromTarget, arrow,
70 target, &completion));
71 completion.Wait();
72 }
73
CreateArrowFromArrow(Arrow ** arrow,const Arrow * other)74 void CreateArrowFromArrow(Arrow** arrow, const Arrow* other) {
75 WaitableEvent completion(true, false);
76 task_runner()->PostTask(
77 FROM_HERE, base::Bind(&BackgroundThread::DoCreateArrowFromArrow, arrow,
78 other, &completion));
79 completion.Wait();
80 }
81
DeleteTarget(Target * object)82 void DeleteTarget(Target* object) {
83 WaitableEvent completion(true, false);
84 task_runner()->PostTask(
85 FROM_HERE,
86 base::Bind(&BackgroundThread::DoDeleteTarget, object, &completion));
87 completion.Wait();
88 }
89
CopyAndAssignArrow(Arrow * object)90 void CopyAndAssignArrow(Arrow* object) {
91 WaitableEvent completion(true, false);
92 task_runner()->PostTask(
93 FROM_HERE, base::Bind(&BackgroundThread::DoCopyAndAssignArrow, object,
94 &completion));
95 completion.Wait();
96 }
97
CopyAndAssignArrowBase(Arrow * object)98 void CopyAndAssignArrowBase(Arrow* object) {
99 WaitableEvent completion(true, false);
100 task_runner()->PostTask(
101 FROM_HERE, base::Bind(&BackgroundThread::DoCopyAndAssignArrowBase,
102 object, &completion));
103 completion.Wait();
104 }
105
DeleteArrow(Arrow * object)106 void DeleteArrow(Arrow* object) {
107 WaitableEvent completion(true, false);
108 task_runner()->PostTask(
109 FROM_HERE,
110 base::Bind(&BackgroundThread::DoDeleteArrow, object, &completion));
111 completion.Wait();
112 }
113
DeRef(const Arrow * arrow)114 Target* DeRef(const Arrow* arrow) {
115 WaitableEvent completion(true, false);
116 Target* result = NULL;
117 task_runner()->PostTask(FROM_HERE, base::Bind(&BackgroundThread::DoDeRef,
118 arrow, &result, &completion));
119 completion.Wait();
120 return result;
121 }
122
123 protected:
DoCreateArrowFromArrow(Arrow ** arrow,const Arrow * other,WaitableEvent * completion)124 static void DoCreateArrowFromArrow(Arrow** arrow,
125 const Arrow* other,
126 WaitableEvent* completion) {
127 *arrow = new Arrow;
128 **arrow = *other;
129 completion->Signal();
130 }
131
DoCreateArrowFromTarget(Arrow ** arrow,Target * target,WaitableEvent * completion)132 static void DoCreateArrowFromTarget(Arrow** arrow,
133 Target* target,
134 WaitableEvent* completion) {
135 *arrow = new Arrow;
136 (*arrow)->target = target->AsWeakPtr();
137 completion->Signal();
138 }
139
DoDeRef(const Arrow * arrow,Target ** result,WaitableEvent * completion)140 static void DoDeRef(const Arrow* arrow,
141 Target** result,
142 WaitableEvent* completion) {
143 *result = arrow->target.get();
144 completion->Signal();
145 }
146
DoDeleteTarget(Target * object,WaitableEvent * completion)147 static void DoDeleteTarget(Target* object, WaitableEvent* completion) {
148 delete object;
149 completion->Signal();
150 }
151
DoCopyAndAssignArrow(Arrow * object,WaitableEvent * completion)152 static void DoCopyAndAssignArrow(Arrow* object, WaitableEvent* completion) {
153 // Copy constructor.
154 Arrow a = *object;
155 // Assignment operator.
156 *object = a;
157 completion->Signal();
158 }
159
DoCopyAndAssignArrowBase(Arrow * object,WaitableEvent * completion)160 static void DoCopyAndAssignArrowBase(
161 Arrow* object,
162 WaitableEvent* completion) {
163 // Copy constructor.
164 WeakPtr<TargetBase> b = object->target;
165 // Assignment operator.
166 WeakPtr<TargetBase> c;
167 c = object->target;
168 completion->Signal();
169 }
170
DoDeleteArrow(Arrow * object,WaitableEvent * completion)171 static void DoDeleteArrow(Arrow* object, WaitableEvent* completion) {
172 delete object;
173 completion->Signal();
174 }
175 };
176
177 } // namespace
178
TEST(WeakPtrFactoryTest,Basic)179 TEST(WeakPtrFactoryTest, Basic) {
180 int data;
181 WeakPtrFactory<int> factory(&data);
182 WeakPtr<int> ptr = factory.GetWeakPtr();
183 EXPECT_EQ(&data, ptr.get());
184 }
185
TEST(WeakPtrFactoryTest,Comparison)186 TEST(WeakPtrFactoryTest, Comparison) {
187 int data;
188 WeakPtrFactory<int> factory(&data);
189 WeakPtr<int> ptr = factory.GetWeakPtr();
190 WeakPtr<int> ptr2 = ptr;
191 EXPECT_EQ(ptr.get(), ptr2.get());
192 }
193
TEST(WeakPtrFactoryTest,OutOfScope)194 TEST(WeakPtrFactoryTest, OutOfScope) {
195 WeakPtr<int> ptr;
196 EXPECT_EQ(NULL, ptr.get());
197 {
198 int data;
199 WeakPtrFactory<int> factory(&data);
200 ptr = factory.GetWeakPtr();
201 }
202 EXPECT_EQ(NULL, ptr.get());
203 }
204
TEST(WeakPtrFactoryTest,Multiple)205 TEST(WeakPtrFactoryTest, Multiple) {
206 WeakPtr<int> a, b;
207 {
208 int data;
209 WeakPtrFactory<int> factory(&data);
210 a = factory.GetWeakPtr();
211 b = factory.GetWeakPtr();
212 EXPECT_EQ(&data, a.get());
213 EXPECT_EQ(&data, b.get());
214 }
215 EXPECT_EQ(NULL, a.get());
216 EXPECT_EQ(NULL, b.get());
217 }
218
TEST(WeakPtrFactoryTest,MultipleStaged)219 TEST(WeakPtrFactoryTest, MultipleStaged) {
220 WeakPtr<int> a;
221 {
222 int data;
223 WeakPtrFactory<int> factory(&data);
224 a = factory.GetWeakPtr();
225 {
226 WeakPtr<int> b = factory.GetWeakPtr();
227 }
228 EXPECT_TRUE(NULL != a.get());
229 }
230 EXPECT_EQ(NULL, a.get());
231 }
232
TEST(WeakPtrFactoryTest,Dereference)233 TEST(WeakPtrFactoryTest, Dereference) {
234 Base data;
235 data.member = "123456";
236 WeakPtrFactory<Base> factory(&data);
237 WeakPtr<Base> ptr = factory.GetWeakPtr();
238 EXPECT_EQ(&data, ptr.get());
239 EXPECT_EQ(data.member, (*ptr).member);
240 EXPECT_EQ(data.member, ptr->member);
241 }
242
TEST(WeakPtrFactoryTest,UpCast)243 TEST(WeakPtrFactoryTest, UpCast) {
244 Derived data;
245 WeakPtrFactory<Derived> factory(&data);
246 WeakPtr<Base> ptr = factory.GetWeakPtr();
247 ptr = factory.GetWeakPtr();
248 EXPECT_EQ(ptr.get(), &data);
249 }
250
TEST(WeakPtrTest,SupportsWeakPtr)251 TEST(WeakPtrTest, SupportsWeakPtr) {
252 Target target;
253 WeakPtr<Target> ptr = target.AsWeakPtr();
254 EXPECT_EQ(&target, ptr.get());
255 }
256
TEST(WeakPtrTest,DerivedTarget)257 TEST(WeakPtrTest, DerivedTarget) {
258 DerivedTarget target;
259 WeakPtr<DerivedTarget> ptr = AsWeakPtr(&target);
260 EXPECT_EQ(&target, ptr.get());
261 }
262
TEST(WeakPtrTest,InvalidateWeakPtrs)263 TEST(WeakPtrTest, InvalidateWeakPtrs) {
264 int data;
265 WeakPtrFactory<int> factory(&data);
266 WeakPtr<int> ptr = factory.GetWeakPtr();
267 EXPECT_EQ(&data, ptr.get());
268 EXPECT_TRUE(factory.HasWeakPtrs());
269 factory.InvalidateWeakPtrs();
270 EXPECT_EQ(NULL, ptr.get());
271 EXPECT_FALSE(factory.HasWeakPtrs());
272
273 // Test that the factory can create new weak pointers after a
274 // InvalidateWeakPtrs call, and they remain valid until the next
275 // InvalidateWeakPtrs call.
276 WeakPtr<int> ptr2 = factory.GetWeakPtr();
277 EXPECT_EQ(&data, ptr2.get());
278 EXPECT_TRUE(factory.HasWeakPtrs());
279 factory.InvalidateWeakPtrs();
280 EXPECT_EQ(NULL, ptr2.get());
281 EXPECT_FALSE(factory.HasWeakPtrs());
282 }
283
TEST(WeakPtrTest,HasWeakPtrs)284 TEST(WeakPtrTest, HasWeakPtrs) {
285 int data;
286 WeakPtrFactory<int> factory(&data);
287 {
288 WeakPtr<int> ptr = factory.GetWeakPtr();
289 EXPECT_TRUE(factory.HasWeakPtrs());
290 }
291 EXPECT_FALSE(factory.HasWeakPtrs());
292 }
293
TEST(WeakPtrTest,ObjectAndWeakPtrOnDifferentThreads)294 TEST(WeakPtrTest, ObjectAndWeakPtrOnDifferentThreads) {
295 // Test that it is OK to create an object that supports WeakPtr on one thread,
296 // but use it on another. This tests that we do not trip runtime checks that
297 // ensure that a WeakPtr is not used by multiple threads.
298 scoped_ptr<Target> target(OffThreadObjectCreator<Target>::NewObject());
299 WeakPtr<Target> weak_ptr = target->AsWeakPtr();
300 EXPECT_EQ(target.get(), weak_ptr.get());
301 }
302
TEST(WeakPtrTest,WeakPtrInitiateAndUseOnDifferentThreads)303 TEST(WeakPtrTest, WeakPtrInitiateAndUseOnDifferentThreads) {
304 // Test that it is OK to create an object that has a WeakPtr member on one
305 // thread, but use it on another. This tests that we do not trip runtime
306 // checks that ensure that a WeakPtr is not used by multiple threads.
307 scoped_ptr<Arrow> arrow(OffThreadObjectCreator<Arrow>::NewObject());
308 Target target;
309 arrow->target = target.AsWeakPtr();
310 EXPECT_EQ(&target, arrow->target.get());
311 }
312
TEST(WeakPtrTest,MoveOwnershipImplicitly)313 TEST(WeakPtrTest, MoveOwnershipImplicitly) {
314 // Move object ownership to another thread by releasing all weak pointers
315 // on the original thread first, and then establish WeakPtr on a different
316 // thread.
317 BackgroundThread background;
318 background.Start();
319
320 Target* target = new Target();
321 {
322 WeakPtr<Target> weak_ptr = target->AsWeakPtr();
323 // Main thread deletes the WeakPtr, then the thread ownership of the
324 // object can be implicitly moved.
325 }
326 Arrow* arrow;
327
328 // Background thread creates WeakPtr(and implicitly owns the object).
329 background.CreateArrowFromTarget(&arrow, target);
330 EXPECT_EQ(background.DeRef(arrow), target);
331
332 {
333 // Main thread creates another WeakPtr, but this does not trigger implicitly
334 // thread ownership move.
335 Arrow arrow;
336 arrow.target = target->AsWeakPtr();
337
338 // The new WeakPtr is owned by background thread.
339 EXPECT_EQ(target, background.DeRef(&arrow));
340 }
341
342 // Target can only be deleted on background thread.
343 background.DeleteTarget(target);
344 background.DeleteArrow(arrow);
345 }
346
TEST(WeakPtrTest,MoveOwnershipOfUnreferencedObject)347 TEST(WeakPtrTest, MoveOwnershipOfUnreferencedObject) {
348 BackgroundThread background;
349 background.Start();
350
351 Arrow* arrow;
352 {
353 Target target;
354 // Background thread creates WeakPtr.
355 background.CreateArrowFromTarget(&arrow, &target);
356
357 // Bind to background thread.
358 EXPECT_EQ(&target, background.DeRef(arrow));
359
360 // Release the only WeakPtr.
361 arrow->target.reset();
362
363 // Now we should be able to create a new reference from this thread.
364 arrow->target = target.AsWeakPtr();
365
366 // Re-bind to main thread.
367 EXPECT_EQ(&target, arrow->target.get());
368
369 // And the main thread can now delete the target.
370 }
371
372 delete arrow;
373 }
374
TEST(WeakPtrTest,MoveOwnershipAfterInvalidate)375 TEST(WeakPtrTest, MoveOwnershipAfterInvalidate) {
376 BackgroundThread background;
377 background.Start();
378
379 Arrow arrow;
380 scoped_ptr<TargetWithFactory> target(new TargetWithFactory);
381
382 // Bind to main thread.
383 arrow.target = target->factory.GetWeakPtr();
384 EXPECT_EQ(target.get(), arrow.target.get());
385
386 target->factory.InvalidateWeakPtrs();
387 EXPECT_EQ(NULL, arrow.target.get());
388
389 arrow.target = target->factory.GetWeakPtr();
390 // Re-bind to background thread.
391 EXPECT_EQ(target.get(), background.DeRef(&arrow));
392
393 // And the background thread can now delete the target.
394 background.DeleteTarget(target.release());
395 }
396
TEST(WeakPtrTest,MainThreadRefOutlivesBackgroundThreadRef)397 TEST(WeakPtrTest, MainThreadRefOutlivesBackgroundThreadRef) {
398 // Originating thread has a WeakPtr that outlives others.
399 // - Main thread creates a WeakPtr
400 // - Background thread creates a WeakPtr copy from the one in main thread
401 // - Destruct the WeakPtr on background thread
402 // - Destruct the WeakPtr on main thread
403 BackgroundThread background;
404 background.Start();
405
406 Target target;
407 Arrow arrow;
408 arrow.target = target.AsWeakPtr();
409
410 Arrow* arrow_copy;
411 background.CreateArrowFromArrow(&arrow_copy, &arrow);
412 EXPECT_EQ(arrow_copy->target.get(), &target);
413 background.DeleteArrow(arrow_copy);
414 }
415
TEST(WeakPtrTest,BackgroundThreadRefOutlivesMainThreadRef)416 TEST(WeakPtrTest, BackgroundThreadRefOutlivesMainThreadRef) {
417 // Originating thread drops all references before another thread.
418 // - Main thread creates a WeakPtr and passes copy to background thread
419 // - Destruct the pointer on main thread
420 // - Destruct the pointer on background thread
421 BackgroundThread background;
422 background.Start();
423
424 Target target;
425 Arrow* arrow_copy;
426 {
427 Arrow arrow;
428 arrow.target = target.AsWeakPtr();
429 background.CreateArrowFromArrow(&arrow_copy, &arrow);
430 }
431 EXPECT_EQ(arrow_copy->target.get(), &target);
432 background.DeleteArrow(arrow_copy);
433 }
434
TEST(WeakPtrTest,OwnerThreadDeletesObject)435 TEST(WeakPtrTest, OwnerThreadDeletesObject) {
436 // Originating thread invalidates WeakPtrs while its held by other thread.
437 // - Main thread creates WeakPtr and passes Copy to background thread
438 // - Object gets destroyed on main thread
439 // (invalidates WeakPtr on background thread)
440 // - WeakPtr gets destroyed on Thread B
441 BackgroundThread background;
442 background.Start();
443 Arrow* arrow_copy;
444 {
445 Target target;
446 Arrow arrow;
447 arrow.target = target.AsWeakPtr();
448 background.CreateArrowFromArrow(&arrow_copy, &arrow);
449 }
450 EXPECT_EQ(NULL, arrow_copy->target.get());
451 background.DeleteArrow(arrow_copy);
452 }
453
TEST(WeakPtrTest,NonOwnerThreadCanCopyAndAssignWeakPtr)454 TEST(WeakPtrTest, NonOwnerThreadCanCopyAndAssignWeakPtr) {
455 // Main thread creates a Target object.
456 Target target;
457 // Main thread creates an arrow referencing the Target.
458 Arrow *arrow = new Arrow();
459 arrow->target = target.AsWeakPtr();
460
461 // Background can copy and assign arrow (as well as the WeakPtr inside).
462 BackgroundThread background;
463 background.Start();
464 background.CopyAndAssignArrow(arrow);
465 background.DeleteArrow(arrow);
466 }
467
TEST(WeakPtrTest,NonOwnerThreadCanCopyAndAssignWeakPtrBase)468 TEST(WeakPtrTest, NonOwnerThreadCanCopyAndAssignWeakPtrBase) {
469 // Main thread creates a Target object.
470 Target target;
471 // Main thread creates an arrow referencing the Target.
472 Arrow *arrow = new Arrow();
473 arrow->target = target.AsWeakPtr();
474
475 // Background can copy and assign arrow's WeakPtr to a base class WeakPtr.
476 BackgroundThread background;
477 background.Start();
478 background.CopyAndAssignArrowBase(arrow);
479 background.DeleteArrow(arrow);
480 }
481
TEST(WeakPtrTest,NonOwnerThreadCanDeleteWeakPtr)482 TEST(WeakPtrTest, NonOwnerThreadCanDeleteWeakPtr) {
483 // Main thread creates a Target object.
484 Target target;
485 // Main thread creates an arrow referencing the Target.
486 Arrow* arrow = new Arrow();
487 arrow->target = target.AsWeakPtr();
488
489 // Background can delete arrow (as well as the WeakPtr inside).
490 BackgroundThread background;
491 background.Start();
492 background.DeleteArrow(arrow);
493 }
494
495 #if (!defined(NDEBUG) || defined(DCHECK_ALWAYS_ON)) && GTEST_HAS_DEATH_TEST
496
TEST(WeakPtrDeathTest,WeakPtrCopyDoesNotChangeThreadBinding)497 TEST(WeakPtrDeathTest, WeakPtrCopyDoesNotChangeThreadBinding) {
498 // The default style "fast" does not support multi-threaded tests
499 // (introduces deadlock on Linux).
500 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
501
502 BackgroundThread background;
503 background.Start();
504
505 // Main thread creates a Target object.
506 Target target;
507 // Main thread creates an arrow referencing the Target.
508 Arrow arrow;
509 arrow.target = target.AsWeakPtr();
510
511 // Background copies the WeakPtr.
512 Arrow* arrow_copy;
513 background.CreateArrowFromArrow(&arrow_copy, &arrow);
514
515 // The copy is still bound to main thread so I can deref.
516 EXPECT_EQ(arrow.target.get(), arrow_copy->target.get());
517
518 // Although background thread created the copy, it can not deref the copied
519 // WeakPtr.
520 ASSERT_DEATH(background.DeRef(arrow_copy), "");
521
522 background.DeleteArrow(arrow_copy);
523 }
524
TEST(WeakPtrDeathTest,NonOwnerThreadDereferencesWeakPtrAfterReference)525 TEST(WeakPtrDeathTest, NonOwnerThreadDereferencesWeakPtrAfterReference) {
526 // The default style "fast" does not support multi-threaded tests
527 // (introduces deadlock on Linux).
528 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
529
530 // Main thread creates a Target object.
531 Target target;
532
533 // Main thread creates an arrow referencing the Target (so target's
534 // thread ownership can not be implicitly moved).
535 Arrow arrow;
536 arrow.target = target.AsWeakPtr();
537 arrow.target.get();
538
539 // Background thread tries to deref target, which violates thread ownership.
540 BackgroundThread background;
541 background.Start();
542 ASSERT_DEATH(background.DeRef(&arrow), "");
543 }
544
TEST(WeakPtrDeathTest,NonOwnerThreadDeletesWeakPtrAfterReference)545 TEST(WeakPtrDeathTest, NonOwnerThreadDeletesWeakPtrAfterReference) {
546 // The default style "fast" does not support multi-threaded tests
547 // (introduces deadlock on Linux).
548 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
549
550 scoped_ptr<Target> target(new Target());
551
552 // Main thread creates an arrow referencing the Target.
553 Arrow arrow;
554 arrow.target = target->AsWeakPtr();
555
556 // Background thread tries to deref target, binding it to the thread.
557 BackgroundThread background;
558 background.Start();
559 background.DeRef(&arrow);
560
561 // Main thread deletes Target, violating thread binding.
562 ASSERT_DEATH(target.reset(), "");
563
564 // |target.reset()| died so |target| still holds the object, so we
565 // must pass it to the background thread to teardown.
566 background.DeleteTarget(target.release());
567 }
568
TEST(WeakPtrDeathTest,NonOwnerThreadDeletesObjectAfterReference)569 TEST(WeakPtrDeathTest, NonOwnerThreadDeletesObjectAfterReference) {
570 // The default style "fast" does not support multi-threaded tests
571 // (introduces deadlock on Linux).
572 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
573
574 scoped_ptr<Target> target(new Target());
575
576 // Main thread creates an arrow referencing the Target, and references it, so
577 // that it becomes bound to the thread.
578 Arrow arrow;
579 arrow.target = target->AsWeakPtr();
580 arrow.target.get();
581
582 // Background thread tries to delete target, volating thread binding.
583 BackgroundThread background;
584 background.Start();
585 ASSERT_DEATH(background.DeleteTarget(target.release()), "");
586 }
587
TEST(WeakPtrDeathTest,NonOwnerThreadReferencesObjectAfterDeletion)588 TEST(WeakPtrDeathTest, NonOwnerThreadReferencesObjectAfterDeletion) {
589 // The default style "fast" does not support multi-threaded tests
590 // (introduces deadlock on Linux).
591 ::testing::FLAGS_gtest_death_test_style = "threadsafe";
592
593 scoped_ptr<Target> target(new Target());
594
595 // Main thread creates an arrow referencing the Target.
596 Arrow arrow;
597 arrow.target = target->AsWeakPtr();
598
599 // Background thread tries to delete target, binding the object to the thread.
600 BackgroundThread background;
601 background.Start();
602 background.DeleteTarget(target.release());
603
604 // Main thread attempts to dereference the target, violating thread binding.
605 ASSERT_DEATH(arrow.target.get(), "");
606 }
607
608 #endif
609
610 } // namespace base
611