Fix passing RequestContext to executor thread
[folly.git] / folly / futures / test / FutureTest.cpp
1 /*
2  * Copyright 2015 Facebook, Inc.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *   http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <gtest/gtest.h>
18
19 #include <folly/futures/Future.h>
20 #include <folly/Memory.h>
21 #include <folly/Executor.h>
22 #include <folly/dynamic.h>
23 #include <folly/Baton.h>
24
25 #include <algorithm>
26 #include <atomic>
27 #include <memory>
28 #include <numeric>
29 #include <string>
30 #include <thread>
31 #include <type_traits>
32 #include <unistd.h>
33
34 using namespace folly;
35
36 #define EXPECT_TYPE(x, T) \
37   EXPECT_TRUE((std::is_same<decltype(x), T>::value))
38
39 typedef FutureException eggs_t;
40 static eggs_t eggs("eggs");
41
42 // Future
43
44 TEST(Future, onError) {
45   bool theFlag = false;
46   auto flag = [&]{ theFlag = true; };
47 #define EXPECT_FLAG() \
48   do { \
49     EXPECT_TRUE(theFlag); \
50     theFlag = false; \
51   } while(0);
52
53 #define EXPECT_NO_FLAG() \
54   do { \
55     EXPECT_FALSE(theFlag); \
56     theFlag = false; \
57   } while(0);
58
59   // By reference
60   {
61     auto f = makeFuture()
62       .then([] { throw eggs; })
63       .onError([&] (eggs_t& e) { flag(); });
64     EXPECT_FLAG();
65     EXPECT_NO_THROW(f.value());
66   }
67
68   {
69     auto f = makeFuture()
70       .then([] { throw eggs; })
71       .onError([&] (eggs_t& e) { flag(); return makeFuture(); });
72     EXPECT_FLAG();
73     EXPECT_NO_THROW(f.value());
74   }
75
76   // By value
77   {
78     auto f = makeFuture()
79       .then([] { throw eggs; })
80       .onError([&] (eggs_t e) { flag(); });
81     EXPECT_FLAG();
82     EXPECT_NO_THROW(f.value());
83   }
84
85   {
86     auto f = makeFuture()
87       .then([] { throw eggs; })
88       .onError([&] (eggs_t e) { flag(); return makeFuture(); });
89     EXPECT_FLAG();
90     EXPECT_NO_THROW(f.value());
91   }
92
93   // Polymorphic
94   {
95     auto f = makeFuture()
96       .then([] { throw eggs; })
97       .onError([&] (std::exception& e) { flag(); });
98     EXPECT_FLAG();
99     EXPECT_NO_THROW(f.value());
100   }
101
102   {
103     auto f = makeFuture()
104       .then([] { throw eggs; })
105       .onError([&] (std::exception& e) { flag(); return makeFuture(); });
106     EXPECT_FLAG();
107     EXPECT_NO_THROW(f.value());
108   }
109
110   // Non-exceptions
111   {
112     auto f = makeFuture()
113       .then([] { throw -1; })
114       .onError([&] (int e) { flag(); });
115     EXPECT_FLAG();
116     EXPECT_NO_THROW(f.value());
117   }
118
119   {
120     auto f = makeFuture()
121       .then([] { throw -1; })
122       .onError([&] (int e) { flag(); return makeFuture(); });
123     EXPECT_FLAG();
124     EXPECT_NO_THROW(f.value());
125   }
126
127   // Mutable lambda
128   {
129     auto f = makeFuture()
130       .then([] { throw eggs; })
131       .onError([&] (eggs_t& e) mutable { flag(); });
132     EXPECT_FLAG();
133     EXPECT_NO_THROW(f.value());
134   }
135
136   {
137     auto f = makeFuture()
138       .then([] { throw eggs; })
139       .onError([&] (eggs_t& e) mutable { flag(); return makeFuture(); });
140     EXPECT_FLAG();
141     EXPECT_NO_THROW(f.value());
142   }
143
144   // No throw
145   {
146     auto f = makeFuture()
147       .then([] { return 42; })
148       .onError([&] (eggs_t& e) { flag(); return -1; });
149     EXPECT_NO_FLAG();
150     EXPECT_EQ(42, f.value());
151   }
152
153   {
154     auto f = makeFuture()
155       .then([] { return 42; })
156       .onError([&] (eggs_t& e) { flag(); return makeFuture<int>(-1); });
157     EXPECT_NO_FLAG();
158     EXPECT_EQ(42, f.value());
159   }
160
161   // Catch different exception
162   {
163     auto f = makeFuture()
164       .then([] { throw eggs; })
165       .onError([&] (std::runtime_error& e) { flag(); });
166     EXPECT_NO_FLAG();
167     EXPECT_THROW(f.value(), eggs_t);
168   }
169
170   {
171     auto f = makeFuture()
172       .then([] { throw eggs; })
173       .onError([&] (std::runtime_error& e) { flag(); return makeFuture(); });
174     EXPECT_NO_FLAG();
175     EXPECT_THROW(f.value(), eggs_t);
176   }
177
178   // Returned value propagates
179   {
180     auto f = makeFuture()
181       .then([] { throw eggs; return 0; })
182       .onError([&] (eggs_t& e) { return 42; });
183     EXPECT_EQ(42, f.value());
184   }
185
186   // Returned future propagates
187   {
188     auto f = makeFuture()
189       .then([] { throw eggs; return 0; })
190       .onError([&] (eggs_t& e) { return makeFuture<int>(42); });
191     EXPECT_EQ(42, f.value());
192   }
193
194   // Throw in callback
195   {
196     auto f = makeFuture()
197       .then([] { throw eggs; return 0; })
198       .onError([&] (eggs_t& e) { throw e; return -1; });
199     EXPECT_THROW(f.value(), eggs_t);
200   }
201
202   {
203     auto f = makeFuture()
204       .then([] { throw eggs; return 0; })
205       .onError([&] (eggs_t& e) { throw e; return makeFuture<int>(-1); });
206     EXPECT_THROW(f.value(), eggs_t);
207   }
208
209   // exception_wrapper, return Future<T>
210   {
211     auto f = makeFuture()
212       .then([] { throw eggs; })
213       .onError([&] (exception_wrapper e) { flag(); return makeFuture(); });
214     EXPECT_FLAG();
215     EXPECT_NO_THROW(f.value());
216   }
217
218   // exception_wrapper, return Future<T> but throw
219   {
220     auto f = makeFuture()
221       .then([]{ throw eggs; return 0; })
222       .onError([&] (exception_wrapper e) {
223         flag();
224         throw eggs;
225         return makeFuture<int>(-1);
226       });
227     EXPECT_FLAG();
228     EXPECT_THROW(f.value(), eggs_t);
229   }
230
231   // exception_wrapper, return T
232   {
233     auto f = makeFuture()
234       .then([]{ throw eggs; return 0; })
235       .onError([&] (exception_wrapper e) {
236         flag();
237         return -1;
238       });
239     EXPECT_FLAG();
240     EXPECT_EQ(-1, f.value());
241   }
242
243   // exception_wrapper, return T but throw
244   {
245     auto f = makeFuture()
246       .then([]{ throw eggs; return 0; })
247       .onError([&] (exception_wrapper e) {
248         flag();
249         throw eggs;
250         return -1;
251       });
252     EXPECT_FLAG();
253     EXPECT_THROW(f.value(), eggs_t);
254   }
255
256   // const exception_wrapper&
257   {
258     auto f = makeFuture()
259       .then([] { throw eggs; })
260       .onError([&] (const exception_wrapper& e) {
261         flag();
262         return makeFuture();
263       });
264     EXPECT_FLAG();
265     EXPECT_NO_THROW(f.value());
266   }
267
268 }
269
270 TEST(Future, special) {
271   EXPECT_FALSE(std::is_copy_constructible<Future<int>>::value);
272   EXPECT_FALSE(std::is_copy_assignable<Future<int>>::value);
273   EXPECT_TRUE(std::is_move_constructible<Future<int>>::value);
274   EXPECT_TRUE(std::is_move_assignable<Future<int>>::value);
275 }
276
277 TEST(Future, then) {
278   auto f = makeFuture<std::string>("0")
279     .then([](){
280       return makeFuture<std::string>("1"); })
281     .then([](Try<std::string>&& t) {
282       return makeFuture(t.value() + ";2"); })
283     .then([](const Try<std::string>&& t) {
284       return makeFuture(t.value() + ";3"); })
285     .then([](Try<std::string>& t) {
286       return makeFuture(t.value() + ";4"); })
287     .then([](const Try<std::string>& t) {
288       return makeFuture(t.value() + ";5"); })
289     .then([](Try<std::string> t) {
290       return makeFuture(t.value() + ";6"); })
291     .then([](const Try<std::string> t) {
292       return makeFuture(t.value() + ";7"); })
293     .then([](std::string&& s) {
294       return makeFuture(s + ";8"); })
295     .then([](const std::string&& s) {
296       return makeFuture(s + ";9"); })
297     .then([](std::string& s) {
298       return makeFuture(s + ";10"); })
299     .then([](const std::string& s) {
300       return makeFuture(s + ";11"); })
301     .then([](std::string s) {
302       return makeFuture(s + ";12"); })
303     .then([](const std::string s) {
304       return makeFuture(s + ";13"); })
305   ;
306   EXPECT_EQ(f.value(), "1;2;3;4;5;6;7;8;9;10;11;12;13");
307 }
308
309 TEST(Future, thenTry) {
310   bool flag = false;
311
312   makeFuture<int>(42).then([&](Try<int>&& t) {
313                               flag = true;
314                               EXPECT_EQ(42, t.value());
315                             });
316   EXPECT_TRUE(flag); flag = false;
317
318   makeFuture<int>(42)
319     .then([](Try<int>&& t) { return t.value(); })
320     .then([&](Try<int>&& t) { flag = true; EXPECT_EQ(42, t.value()); });
321   EXPECT_TRUE(flag); flag = false;
322
323   makeFuture().then([&](Try<void>&& t) { flag = true; t.value(); });
324   EXPECT_TRUE(flag); flag = false;
325
326   Promise<void> p;
327   auto f = p.getFuture().then([&](Try<void>&& t) { flag = true; });
328   EXPECT_FALSE(flag);
329   EXPECT_FALSE(f.isReady());
330   p.setValue();
331   EXPECT_TRUE(flag);
332   EXPECT_TRUE(f.isReady());
333 }
334
335 TEST(Future, thenValue) {
336   bool flag = false;
337   makeFuture<int>(42).then([&](int i){
338     EXPECT_EQ(42, i);
339     flag = true;
340   });
341   EXPECT_TRUE(flag); flag = false;
342
343   makeFuture<int>(42)
344     .then([](int i){ return i; })
345     .then([&](int i) { flag = true; EXPECT_EQ(42, i); });
346   EXPECT_TRUE(flag); flag = false;
347
348   makeFuture().then([&]{
349     flag = true;
350   });
351   EXPECT_TRUE(flag); flag = false;
352
353   auto f = makeFuture<int>(eggs).then([&](int i){});
354   EXPECT_THROW(f.value(), eggs_t);
355
356   f = makeFuture<void>(eggs).then([&]{});
357   EXPECT_THROW(f.value(), eggs_t);
358 }
359
360 TEST(Future, thenValueFuture) {
361   bool flag = false;
362   makeFuture<int>(42)
363     .then([](int i){ return makeFuture<int>(std::move(i)); })
364     .then([&](Try<int>&& t) { flag = true; EXPECT_EQ(42, t.value()); });
365   EXPECT_TRUE(flag); flag = false;
366
367   makeFuture()
368     .then([]{ return makeFuture(); })
369     .then([&](Try<void>&& t) { flag = true; });
370   EXPECT_TRUE(flag); flag = false;
371 }
372
373 static std::string doWorkStatic(Try<std::string>&& t) {
374   return t.value() + ";static";
375 }
376
377 TEST(Future, thenFunction) {
378   struct Worker {
379     std::string doWork(Try<std::string>&& t) {
380       return t.value() + ";class";
381     }
382     static std::string doWorkStatic(Try<std::string>&& t) {
383       return t.value() + ";class-static";
384     }
385   } w;
386
387   auto f = makeFuture<std::string>("start")
388     .then(doWorkStatic)
389     .then(Worker::doWorkStatic)
390     .then(&Worker::doWork, &w);
391
392   EXPECT_EQ(f.value(), "start;static;class-static;class");
393 }
394
395 static Future<std::string> doWorkStaticFuture(Try<std::string>&& t) {
396   return makeFuture(t.value() + ";static");
397 }
398
399 TEST(Future, thenFunctionFuture) {
400   struct Worker {
401     Future<std::string> doWorkFuture(Try<std::string>&& t) {
402       return makeFuture(t.value() + ";class");
403     }
404     static Future<std::string> doWorkStaticFuture(Try<std::string>&& t) {
405       return makeFuture(t.value() + ";class-static");
406     }
407   } w;
408
409   auto f = makeFuture<std::string>("start")
410     .then(doWorkStaticFuture)
411     .then(Worker::doWorkStaticFuture)
412     .then(&Worker::doWorkFuture, &w);
413
414   EXPECT_EQ(f.value(), "start;static;class-static;class");
415 }
416
417 TEST(Future, thenStdFunction) {
418   {
419     std::function<int()> fn = [](){ return 42; };
420     auto f = makeFuture().then(std::move(fn));
421     EXPECT_EQ(f.value(), 42);
422   }
423   {
424     std::function<int(int)> fn = [](int i){ return i + 23; };
425     auto f = makeFuture(19).then(std::move(fn));
426     EXPECT_EQ(f.value(), 42);
427   }
428   {
429     std::function<int(Try<int>&)> fn = [](Try<int>& t){ return t.value() + 2; };
430     auto f = makeFuture(1).then(std::move(fn));
431     EXPECT_EQ(f.value(), 3);
432   }
433   {
434     bool flag = false;
435     std::function<void()> fn = [&flag](){ flag = true; };
436     auto f = makeFuture().then(std::move(fn));
437     EXPECT_TRUE(f.isReady());
438     EXPECT_TRUE(flag);
439   }
440 }
441
442 TEST(Future, thenBind) {
443   auto l = []() {
444     return makeFuture("bind");
445   };
446   auto b = std::bind(l);
447   auto f = makeFuture().then(std::move(b));
448   EXPECT_EQ(f.value(), "bind");
449 }
450
451 TEST(Future, thenBindTry) {
452   auto l = [](Try<std::string>&& t) {
453     return makeFuture(t.value() + ";bind");
454   };
455   auto b = std::bind(l, std::placeholders::_1);
456   auto f = makeFuture<std::string>("start").then(std::move(b));
457
458   EXPECT_EQ(f.value(), "start;bind");
459 }
460
461 TEST(Future, value) {
462   auto f = makeFuture(std::unique_ptr<int>(new int(42)));
463   auto up = std::move(f.value());
464   EXPECT_EQ(42, *up);
465
466   EXPECT_THROW(makeFuture<int>(eggs).value(), eggs_t);
467 }
468
469 TEST(Future, isReady) {
470   Promise<int> p;
471   auto f = p.getFuture();
472   EXPECT_FALSE(f.isReady());
473   p.setValue(42);
474   EXPECT_TRUE(f.isReady());
475   }
476
477 TEST(Future, futureNotReady) {
478   Promise<int> p;
479   Future<int> f = p.getFuture();
480   EXPECT_THROW(f.value(), eggs_t);
481 }
482
483 TEST(Future, hasException) {
484   EXPECT_TRUE(makeFuture<int>(eggs).getTry().hasException());
485   EXPECT_FALSE(makeFuture(42).getTry().hasException());
486 }
487
488 TEST(Future, hasValue) {
489   EXPECT_TRUE(makeFuture(42).getTry().hasValue());
490   EXPECT_FALSE(makeFuture<int>(eggs).getTry().hasValue());
491 }
492
493 TEST(Future, makeFuture) {
494   EXPECT_TYPE(makeFuture(42), Future<int>);
495   EXPECT_EQ(42, makeFuture(42).value());
496
497   EXPECT_TYPE(makeFuture<float>(42), Future<float>);
498   EXPECT_EQ(42, makeFuture<float>(42).value());
499
500   auto fun = [] { return 42; };
501   EXPECT_TYPE(makeFutureWith(fun), Future<int>);
502   EXPECT_EQ(42, makeFutureWith(fun).value());
503
504   auto failfun = []() -> int { throw eggs; };
505   EXPECT_TYPE(makeFutureWith(failfun), Future<int>);
506   EXPECT_THROW(makeFutureWith(failfun).value(), eggs_t);
507
508   EXPECT_TYPE(makeFuture(), Future<void>);
509 }
510
511 TEST(Future, finish) {
512   auto x = std::make_shared<int>(0);
513   {
514     Promise<int> p;
515     auto f = p.getFuture().then([x](Try<int>&& t) { *x = t.value(); });
516
517     // The callback hasn't executed
518     EXPECT_EQ(0, *x);
519
520     // The callback has a reference to x
521     EXPECT_EQ(2, x.use_count());
522
523     p.setValue(42);
524
525     // the callback has executed
526     EXPECT_EQ(42, *x);
527   }
528   // the callback has been destructed
529   // and has released its reference to x
530   EXPECT_EQ(1, x.use_count());
531 }
532
533 TEST(Future, unwrap) {
534   Promise<int> a;
535   Promise<int> b;
536
537   auto fa = a.getFuture();
538   auto fb = b.getFuture();
539
540   bool flag1 = false;
541   bool flag2 = false;
542
543   // do a, then do b, and get the result of a + b.
544   Future<int> f = fa.then([&](Try<int>&& ta) {
545     auto va = ta.value();
546     flag1 = true;
547     return fb.then([va, &flag2](Try<int>&& tb) {
548       flag2 = true;
549       return va + tb.value();
550     });
551   });
552
553   EXPECT_FALSE(flag1);
554   EXPECT_FALSE(flag2);
555   EXPECT_FALSE(f.isReady());
556
557   a.setValue(3);
558   EXPECT_TRUE(flag1);
559   EXPECT_FALSE(flag2);
560   EXPECT_FALSE(f.isReady());
561
562   b.setValue(4);
563   EXPECT_TRUE(flag1);
564   EXPECT_TRUE(flag2);
565   EXPECT_EQ(7, f.value());
566 }
567
568 TEST(Future, throwCaughtInImmediateThen) {
569   // Neither of these should throw "Promise already satisfied"
570   makeFuture().then(
571     [=](Try<void>&&) -> int { throw std::exception(); });
572   makeFuture().then(
573     [=](Try<void>&&) -> Future<int> { throw std::exception(); });
574 }
575
576 TEST(Future, throwIfFailed) {
577   makeFuture<void>(eggs)
578     .then([=](Try<void>&& t) {
579       EXPECT_THROW(t.throwIfFailed(), eggs_t);
580     });
581   makeFuture()
582     .then([=](Try<void>&& t) {
583       EXPECT_NO_THROW(t.throwIfFailed());
584     });
585
586   makeFuture<int>(eggs)
587     .then([=](Try<int>&& t) {
588       EXPECT_THROW(t.throwIfFailed(), eggs_t);
589     });
590   makeFuture<int>(42)
591     .then([=](Try<int>&& t) {
592       EXPECT_NO_THROW(t.throwIfFailed());
593     });
594 }
595
596 TEST(Future, getFutureAfterSetValue) {
597   Promise<int> p;
598   p.setValue(42);
599   EXPECT_EQ(42, p.getFuture().value());
600 }
601
602 TEST(Future, getFutureAfterSetException) {
603   Promise<void> p;
604   p.setWith([]() -> void { throw std::logic_error("foo"); });
605   EXPECT_THROW(p.getFuture().value(), std::logic_error);
606 }
607
608 TEST(Future, detachRace) {
609   // Task #5438209
610   // This test is designed to detect a race that was in Core::detachOne()
611   // where detached_ was incremented and then tested, and that
612   // allowed a race where both Promise and Future would think they were the
613   // second and both try to delete. This showed up at scale but was very
614   // difficult to reliably repro in a test. As it is, this only fails about
615   // once in every 1,000 executions. Doing this 1,000 times is going to make a
616   // slow test so I won't do that but if it ever fails, take it seriously, and
617   // run the test binary with "--gtest_repeat=10000 --gtest_filter=*detachRace"
618   // (Don't forget to enable ASAN)
619   auto p = folly::make_unique<Promise<bool>>();
620   auto f = folly::make_unique<Future<bool>>(p->getFuture());
621   folly::Baton<> baton;
622   std::thread t1([&]{
623     baton.post();
624     p.reset();
625   });
626   baton.wait();
627   f.reset();
628   t1.join();
629 }
630
631 // Test of handling of a circular dependency. It's never recommended
632 // to have one because of possible memory leaks. Here we test that
633 // we can handle freeing of the Future while it is running.
634 TEST(Future, CircularDependencySharedPtrSelfReset) {
635   Promise<int64_t> promise;
636   auto ptr = std::make_shared<Future<int64_t>>(promise.getFuture());
637
638   ptr->then(
639     [ptr] (folly::Try<int64_t>&& uid) mutable {
640       EXPECT_EQ(1, ptr.use_count());
641
642       // Leaving no references to ourselves.
643       ptr.reset();
644       EXPECT_EQ(0, ptr.use_count());
645     }
646   );
647
648   EXPECT_EQ(2, ptr.use_count());
649
650   ptr.reset();
651
652   promise.setValue(1);
653 }
654
655 TEST(Future, Constructor) {
656   auto f1 = []() -> Future<int> { return Future<int>(3); }();
657   EXPECT_EQ(f1.value(), 3);
658   auto f2 = []() -> Future<void> { return Future<void>(); }();
659   EXPECT_NO_THROW(f2.value());
660 }
661
662 TEST(Future, ImplicitConstructor) {
663   auto f1 = []() -> Future<int> { return 3; }();
664   EXPECT_EQ(f1.value(), 3);
665   // Unfortunately, the C++ standard does not allow the
666   // following implicit conversion to work:
667   //auto f2 = []() -> Future<void> { }();
668 }
669
670 TEST(Future, thenDynamic) {
671   // folly::dynamic has a constructor that takes any T, this test makes
672   // sure that we call the then lambda with folly::dynamic and not
673   // Try<folly::dynamic> because that then fails to compile
674   Promise<folly::dynamic> p;
675   Future<folly::dynamic> f = p.getFuture().then(
676       [](const folly::dynamic& d) {
677         return folly::dynamic(d.asInt() + 3);
678       }
679   );
680   p.setValue(2);
681   EXPECT_EQ(f.get(), 5);
682 }
683
684 TEST(Future, RequestContext) {
685   class NewThreadExecutor : public Executor {
686    public:
687     ~NewThreadExecutor() override {
688       std::for_each(v_.begin(), v_.end(), [](std::thread& t){ t.join(); });
689     }
690     void add(Func f) override {
691       if (throwsOnAdd_) { throw std::exception(); }
692       v_.emplace_back(std::move(f));
693     }
694     void addWithPriority(Func f, int8_t prio) override { add(std::move(f)); }
695     uint8_t getNumPriorities() const override { return numPriorities_; }
696
697     void setHandlesPriorities() { numPriorities_ = 2; }
698     void setThrowsOnAdd() { throwsOnAdd_ = true; }
699    private:
700     std::vector<std::thread> v_;
701     uint8_t numPriorities_ = 1;
702     bool throwsOnAdd_ = false;
703   };
704
705   struct MyRequestData : RequestData {
706     MyRequestData(bool value = false) : value(value) {}
707     bool value;
708   };
709
710   NewThreadExecutor e;
711   RequestContext::create();
712   RequestContext::get()->setContextData("key",
713       folly::make_unique<MyRequestData>(true));
714   auto checker = [](int lineno) {
715     return [lineno](Try<int>&& t) {
716       auto d = static_cast<MyRequestData*>(
717         RequestContext::get()->getContextData("key"));
718       EXPECT_TRUE(d && d->value) << "on line " << lineno;
719     };
720   };
721
722   makeFuture(1).via(&e).then(checker(__LINE__));
723
724   e.setHandlesPriorities();
725   makeFuture(2).via(&e).then(checker(__LINE__));
726
727   Promise<int> p1, p2;
728   p1.getFuture().then(checker(__LINE__));
729
730   e.setThrowsOnAdd();
731   p2.getFuture().via(&e).then(checker(__LINE__));
732
733   RequestContext::create();
734   p1.setValue(3);
735   p2.setValue(4);
736 }