OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "base/basictypes.h" | 5 #include "base/basictypes.h" |
6 #include "base/bind.h" | 6 #include "base/bind.h" |
7 #include "base/bind_helpers.h" | 7 #include "base/bind_helpers.h" |
8 #include "base/file_util.h" | 8 #include "base/file_util.h" |
| 9 #include "base/string_util.h" |
| 10 #include "base/stringprintf.h" |
9 #include "base/threading/platform_thread.h" | 11 #include "base/threading/platform_thread.h" |
10 #include "base/timer.h" | 12 #include "base/timer.h" |
11 #include "base/string_util.h" | |
12 #include "base/stringprintf.h" | |
13 #include "net/base/completion_callback.h" | 13 #include "net/base/completion_callback.h" |
14 #include "net/base/io_buffer.h" | 14 #include "net/base/io_buffer.h" |
15 #include "net/base/net_errors.h" | 15 #include "net/base/net_errors.h" |
16 #include "net/base/test_completion_callback.h" | 16 #include "net/base/test_completion_callback.h" |
17 #include "net/disk_cache/backend_impl.h" | 17 #include "net/disk_cache/backend_impl.h" |
18 #include "net/disk_cache/disk_cache_test_base.h" | 18 #include "net/disk_cache/disk_cache_test_base.h" |
19 #include "net/disk_cache/disk_cache_test_util.h" | 19 #include "net/disk_cache/disk_cache_test_util.h" |
20 #include "net/disk_cache/entry_impl.h" | 20 #include "net/disk_cache/entry_impl.h" |
21 #include "net/disk_cache/mem_entry_impl.h" | 21 #include "net/disk_cache/mem_entry_impl.h" |
22 #include "net/disk_cache/simple/simple_entry_format.h" | 22 #include "net/disk_cache/simple/simple_entry_format.h" |
| 23 #include "net/disk_cache/simple/simple_entry_impl.h" |
23 #include "net/disk_cache/simple/simple_util.h" | 24 #include "net/disk_cache/simple/simple_util.h" |
24 #include "testing/gtest/include/gtest/gtest.h" | 25 #include "testing/gtest/include/gtest/gtest.h" |
25 | 26 |
26 using base::Time; | 27 using base::Time; |
27 | 28 |
28 // Tests that can run with different types of caches. | 29 // Tests that can run with different types of caches. |
29 class DiskCacheEntryTest : public DiskCacheTestWithCache { | 30 class DiskCacheEntryTest : public DiskCacheTestWithCache { |
30 public: | 31 public: |
31 void InternalSyncIOBackground(disk_cache::Entry* entry); | 32 void InternalSyncIOBackground(disk_cache::Entry* entry); |
32 void ExternalSyncIOBackground(disk_cache::Entry* entry); | 33 void ExternalSyncIOBackground(disk_cache::Entry* entry); |
(...skipping 2385 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2418 const base::FilePath entry_path = cache_path_.AppendASCII( | 2419 const base::FilePath entry_path = cache_path_.AppendASCII( |
2419 disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0)); | 2420 disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, 0)); |
2420 const int64 invalid_size = | 2421 const int64 invalid_size = |
2421 disk_cache::simple_util::GetFileSizeFromKeyAndDataSize(key, | 2422 disk_cache::simple_util::GetFileSizeFromKeyAndDataSize(key, |
2422 kTruncationBytes); | 2423 kTruncationBytes); |
2423 EXPECT_TRUE(TruncatePath(entry_path, invalid_size)); | 2424 EXPECT_TRUE(TruncatePath(entry_path, invalid_size)); |
2424 EXPECT_EQ(net::ERR_FAILED, OpenEntry(key, &entry)); | 2425 EXPECT_EQ(net::ERR_FAILED, OpenEntry(key, &entry)); |
2425 DisableIntegrityCheck(); | 2426 DisableIntegrityCheck(); |
2426 } | 2427 } |
2427 | 2428 |
| 2429 TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic) { |
| 2430 // Test sequence: |
| 2431 // Create, Write, Read, Write, Read, Close. |
| 2432 SetSimpleCacheMode(); |
| 2433 InitCache(); |
| 2434 disk_cache::Entry* null = NULL; |
| 2435 const char key[] = "the first key"; |
| 2436 |
| 2437 MessageLoopHelper helper; |
| 2438 CallbackTest callback1(&helper, false); |
| 2439 CallbackTest callback2(&helper, false); |
| 2440 CallbackTest callback3(&helper, false); |
| 2441 CallbackTest callback4(&helper, false); |
| 2442 CallbackTest callback5(&helper, false); |
| 2443 |
| 2444 int expected = 0; |
| 2445 const int kSize1 = 10; |
| 2446 const int kSize2 = 20; |
| 2447 scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize1)); |
| 2448 scoped_refptr<net::IOBuffer> buffer1_read(new net::IOBuffer(kSize1)); |
| 2449 scoped_refptr<net::IOBuffer> buffer2(new net::IOBuffer(kSize2)); |
| 2450 scoped_refptr<net::IOBuffer> buffer2_read(new net::IOBuffer(kSize2)); |
| 2451 CacheTestFillBuffer(buffer1->data(), kSize1, false); |
| 2452 CacheTestFillBuffer(buffer2->data(), kSize2, false); |
| 2453 |
| 2454 disk_cache::Entry* entry = NULL; |
| 2455 // Create is optimistic, must return OK. |
| 2456 ASSERT_EQ(net::OK, |
| 2457 cache_->CreateEntry(key, &entry, |
| 2458 base::Bind(&CallbackTest::Run, |
| 2459 base::Unretained(&callback1)))); |
| 2460 EXPECT_NE(null, entry); |
| 2461 |
| 2462 // This write may or may not be optimistic (it depends if the previous |
| 2463 // optimistic create already finished by the time we call the write here). |
| 2464 int ret = entry->WriteData( |
| 2465 0, 0, buffer1, kSize1, |
| 2466 base::Bind(&CallbackTest::Run, base::Unretained(&callback2)), false); |
| 2467 EXPECT_TRUE(kSize1 == ret || net::ERR_IO_PENDING == ret); |
| 2468 if (net::ERR_IO_PENDING == ret) |
| 2469 expected++; |
| 2470 |
| 2471 // This Read must not be optimistic, since we don't support that yet. |
| 2472 EXPECT_EQ(net::ERR_IO_PENDING, entry->ReadData( |
| 2473 0, 0, buffer1_read, kSize1, |
| 2474 base::Bind(&CallbackTest::Run, base::Unretained(&callback3)))); |
| 2475 expected++; |
| 2476 EXPECT_TRUE(helper.WaitUntilCacheIoFinished(expected)); |
| 2477 EXPECT_EQ(0, memcmp(buffer1->data(), buffer1_read->data(), kSize1)); |
| 2478 |
| 2479 // At this point after waiting, the pending operations queue on the entry |
| 2480 // should be empty, so the next Write operation must run as optimistic. |
| 2481 EXPECT_EQ(kSize2, |
| 2482 entry->WriteData( |
| 2483 0, 0, buffer2, kSize2, |
| 2484 base::Bind(&CallbackTest::Run, |
| 2485 base::Unretained(&callback4)), false)); |
| 2486 |
| 2487 // Lets do another read so we block until both the write and the read |
| 2488 // operation finishes and we can then test for HasOneRef() below. |
| 2489 EXPECT_EQ(net::ERR_IO_PENDING, entry->ReadData( |
| 2490 0, 0, buffer2_read, kSize2, |
| 2491 base::Bind(&CallbackTest::Run, base::Unretained(&callback5)))); |
| 2492 expected++; |
| 2493 |
| 2494 EXPECT_TRUE(helper.WaitUntilCacheIoFinished(expected)); |
| 2495 EXPECT_EQ(0, memcmp(buffer2->data(), buffer2_read->data(), kSize2)); |
| 2496 |
| 2497 // Check that we are not leaking. |
| 2498 EXPECT_NE(entry, null); |
| 2499 EXPECT_TRUE( |
| 2500 static_cast<disk_cache::SimpleEntryImpl*>(entry)->HasOneRef()); |
| 2501 entry->Close(); |
| 2502 entry = NULL; |
| 2503 } |
| 2504 |
| 2505 TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic2) { |
| 2506 // Test sequence: |
| 2507 // Create, Open, Close, Close. |
| 2508 SetSimpleCacheMode(); |
| 2509 InitCache(); |
| 2510 disk_cache::Entry* null = NULL; |
| 2511 const char key[] = "the first key"; |
| 2512 |
| 2513 MessageLoopHelper helper; |
| 2514 CallbackTest callback1(&helper, false); |
| 2515 CallbackTest callback2(&helper, false); |
| 2516 |
| 2517 disk_cache::Entry* entry = NULL; |
| 2518 ASSERT_EQ(net::OK, |
| 2519 cache_->CreateEntry(key, &entry, |
| 2520 base::Bind(&CallbackTest::Run, |
| 2521 base::Unretained(&callback1)))); |
| 2522 EXPECT_NE(null, entry); |
| 2523 |
| 2524 disk_cache::Entry* entry2 = NULL; |
| 2525 EXPECT_EQ(net::ERR_IO_PENDING, |
| 2526 cache_->OpenEntry(key, &entry2, |
| 2527 base::Bind(&CallbackTest::Run, |
| 2528 base::Unretained(&callback2)))); |
| 2529 EXPECT_TRUE(helper.WaitUntilCacheIoFinished(1)); |
| 2530 |
| 2531 EXPECT_NE(null, entry2); |
| 2532 EXPECT_EQ(entry, entry2); |
| 2533 |
| 2534 // We have to call close twice, since we called create and open above. |
| 2535 entry->Close(); |
| 2536 |
| 2537 // Check that we are not leaking. |
| 2538 EXPECT_TRUE( |
| 2539 static_cast<disk_cache::SimpleEntryImpl*>(entry)->HasOneRef()); |
| 2540 entry->Close(); |
| 2541 entry = NULL; |
| 2542 } |
| 2543 |
| 2544 TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic3) { |
| 2545 // Test sequence: |
| 2546 // Create, Close, Open, Close. |
| 2547 SetSimpleCacheMode(); |
| 2548 InitCache(); |
| 2549 disk_cache::Entry* null = NULL; |
| 2550 const char key[] = "the first key"; |
| 2551 |
| 2552 disk_cache::Entry* entry = NULL; |
| 2553 ASSERT_EQ(net::OK, |
| 2554 cache_->CreateEntry(key, &entry, net::CompletionCallback())); |
| 2555 EXPECT_NE(null, entry); |
| 2556 entry->Close(); |
| 2557 |
| 2558 net::TestCompletionCallback cb; |
| 2559 disk_cache::Entry* entry2 = NULL; |
| 2560 EXPECT_EQ(net::ERR_IO_PENDING, |
| 2561 cache_->OpenEntry(key, &entry2, cb.callback())); |
| 2562 EXPECT_EQ(net::OK, cb.GetResult(net::ERR_IO_PENDING)); |
| 2563 |
| 2564 EXPECT_NE(null, entry2); |
| 2565 EXPECT_EQ(entry, entry2); |
| 2566 |
| 2567 // Check that we are not leaking. |
| 2568 EXPECT_TRUE( |
| 2569 static_cast<disk_cache::SimpleEntryImpl*>(entry2)->HasOneRef()); |
| 2570 entry2->Close(); |
| 2571 } |
| 2572 |
| 2573 TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic4) { |
| 2574 // Test sequence: |
| 2575 // Create, Close, Write, Open, Open, Close, Write, Read, Close. |
| 2576 SetSimpleCacheMode(); |
| 2577 InitCache(); |
| 2578 disk_cache::Entry* null = NULL; |
| 2579 const char key[] = "the first key"; |
| 2580 |
| 2581 net::TestCompletionCallback cb; |
| 2582 const int kSize1 = 10; |
| 2583 scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize1)); |
| 2584 CacheTestFillBuffer(buffer1->data(), kSize1, false); |
| 2585 disk_cache::Entry* entry = NULL; |
| 2586 |
| 2587 ASSERT_EQ(net::OK, |
| 2588 cache_->CreateEntry(key, &entry, net::CompletionCallback())); |
| 2589 EXPECT_NE(null, entry); |
| 2590 entry->Close(); |
| 2591 |
| 2592 // Lets do a Write so we block until both the Close and the Write |
| 2593 // operation finishes. Write must fail since we are writing in a closed entry. |
| 2594 EXPECT_EQ(net::ERR_IO_PENDING, entry->WriteData( |
| 2595 0, 0, buffer1, kSize1, cb.callback(), false)); |
| 2596 EXPECT_EQ(net::ERR_FAILED, cb.GetResult(net::ERR_IO_PENDING)); |
| 2597 |
| 2598 // Finish running the pending tasks so that we fully complete the close |
| 2599 // operation and destroy the entry object. |
| 2600 MessageLoop::current()->RunUntilIdle(); |
| 2601 |
| 2602 // At this point the |entry| must have been destroyed, and called |
| 2603 // RemoveSelfFromBackend(). |
| 2604 disk_cache::Entry* entry2 = NULL; |
| 2605 EXPECT_EQ(net::ERR_IO_PENDING, |
| 2606 cache_->OpenEntry(key, &entry2, cb.callback())); |
| 2607 EXPECT_EQ(net::OK, cb.GetResult(net::ERR_IO_PENDING)); |
| 2608 EXPECT_NE(null, entry2); |
| 2609 |
| 2610 disk_cache::Entry* entry3 = NULL; |
| 2611 EXPECT_EQ(net::ERR_IO_PENDING, |
| 2612 cache_->OpenEntry(key, &entry3, cb.callback())); |
| 2613 EXPECT_EQ(net::OK, cb.GetResult(net::ERR_IO_PENDING)); |
| 2614 EXPECT_NE(null, entry3); |
| 2615 EXPECT_EQ(entry2, entry3); |
| 2616 entry3->Close(); |
| 2617 |
| 2618 // The previous Close doesn't actually closes the entry since we opened it |
| 2619 // twice, so the next Write operation must succeed and it must be able to |
| 2620 // perform it optimistically, since there is no operation running on this |
| 2621 // entry. |
| 2622 EXPECT_EQ(kSize1, entry2->WriteData( |
| 2623 0, 0, buffer1, kSize1, net::CompletionCallback(), false)); |
| 2624 |
| 2625 // Lets do another read so we block until both the write and the read |
| 2626 // operation finishes and we can then test for HasOneRef() below. |
| 2627 EXPECT_EQ(net::ERR_IO_PENDING, entry2->ReadData( |
| 2628 0, 0, buffer1, kSize1, cb.callback())); |
| 2629 EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING)); |
| 2630 |
| 2631 // Check that we are not leaking. |
| 2632 EXPECT_TRUE( |
| 2633 static_cast<disk_cache::SimpleEntryImpl*>(entry2)->HasOneRef()); |
| 2634 entry2->Close(); |
| 2635 } |
| 2636 |
| 2637 // This test is flaky because of the race of Create followed by a Doom. |
| 2638 // See test SimpleCacheCreateDoomRace. |
| 2639 TEST_F(DiskCacheEntryTest, DISABLED_SimpleCacheOptimistic5) { |
| 2640 // Test sequence: |
| 2641 // Create, Doom, Write, Read, Close. |
| 2642 SetSimpleCacheMode(); |
| 2643 InitCache(); |
| 2644 disk_cache::Entry* null = NULL; |
| 2645 const char key[] = "the first key"; |
| 2646 |
| 2647 net::TestCompletionCallback cb; |
| 2648 const int kSize1 = 10; |
| 2649 scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize1)); |
| 2650 CacheTestFillBuffer(buffer1->data(), kSize1, false); |
| 2651 disk_cache::Entry* entry = NULL; |
| 2652 |
| 2653 ASSERT_EQ(net::OK, |
| 2654 cache_->CreateEntry(key, &entry, net::CompletionCallback())); |
| 2655 EXPECT_NE(null, entry); |
| 2656 entry->Doom(); |
| 2657 |
| 2658 EXPECT_EQ(net::ERR_IO_PENDING, entry->WriteData( |
| 2659 0, 0, buffer1, kSize1, cb.callback(), false)); |
| 2660 EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING)); |
| 2661 |
| 2662 EXPECT_EQ(net::ERR_IO_PENDING, entry->ReadData( |
| 2663 0, 0, buffer1, kSize1, cb.callback())); |
| 2664 EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING)); |
| 2665 |
| 2666 // Check that we are not leaking. |
| 2667 EXPECT_TRUE( |
| 2668 static_cast<disk_cache::SimpleEntryImpl*>(entry)->HasOneRef()); |
| 2669 entry->Close(); |
| 2670 } |
| 2671 |
| 2672 TEST_F(DiskCacheEntryTest, SimpleCacheOptimistic6) { |
| 2673 // Test sequence: |
| 2674 // Create, Write, Doom, Doom, Read, Doom, Close. |
| 2675 SetSimpleCacheMode(); |
| 2676 InitCache(); |
| 2677 disk_cache::Entry* null = NULL; |
| 2678 const char key[] = "the first key"; |
| 2679 |
| 2680 net::TestCompletionCallback cb; |
| 2681 const int kSize1 = 10; |
| 2682 scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize1)); |
| 2683 scoped_refptr<net::IOBuffer> buffer1_read(new net::IOBuffer(kSize1)); |
| 2684 CacheTestFillBuffer(buffer1->data(), kSize1, false); |
| 2685 disk_cache::Entry* entry = NULL; |
| 2686 |
| 2687 ASSERT_EQ(net::OK, |
| 2688 cache_->CreateEntry(key, &entry, net::CompletionCallback())); |
| 2689 EXPECT_NE(null, entry); |
| 2690 |
| 2691 EXPECT_EQ(net::ERR_IO_PENDING, entry->WriteData( |
| 2692 0, 0, buffer1, kSize1, cb.callback(), false)); |
| 2693 EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING)); |
| 2694 |
| 2695 entry->Doom(); |
| 2696 entry->Doom(); |
| 2697 |
| 2698 // This Read must not be optimistic, since we don't support that yet. |
| 2699 EXPECT_EQ(net::ERR_IO_PENDING, entry->ReadData( |
| 2700 0, 0, buffer1_read, kSize1, cb.callback())); |
| 2701 EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING)); |
| 2702 EXPECT_EQ(0, memcmp(buffer1->data(), buffer1_read->data(), kSize1)); |
| 2703 |
| 2704 entry->Doom(); |
| 2705 |
| 2706 // Check that we are not leaking. |
| 2707 EXPECT_TRUE( |
| 2708 static_cast<disk_cache::SimpleEntryImpl*>(entry)->HasOneRef()); |
| 2709 entry->Close(); |
| 2710 } |
| 2711 |
| 2712 TEST_F(DiskCacheEntryTest, DISABLED_SimpleCacheCreateDoomRace) { |
| 2713 // Test sequence: |
| 2714 // Create, Doom, Write, Close, Check files are not on disk anymore. |
| 2715 SetSimpleCacheMode(); |
| 2716 InitCache(); |
| 2717 disk_cache::Entry* null = NULL; |
| 2718 const char key[] = "the first key"; |
| 2719 |
| 2720 net::TestCompletionCallback cb; |
| 2721 const int kSize1 = 10; |
| 2722 scoped_refptr<net::IOBuffer> buffer1(new net::IOBuffer(kSize1)); |
| 2723 CacheTestFillBuffer(buffer1->data(), kSize1, false); |
| 2724 disk_cache::Entry* entry = NULL; |
| 2725 |
| 2726 ASSERT_EQ(net::OK, |
| 2727 cache_->CreateEntry(key, &entry, net::CompletionCallback())); |
| 2728 EXPECT_NE(null, entry); |
| 2729 |
| 2730 cache_->DoomEntry(key, cb.callback()); |
| 2731 EXPECT_EQ(net::OK, cb.GetResult(net::ERR_IO_PENDING)); |
| 2732 |
| 2733 // Lets do a Write so we block until all operations are done, so we can check |
| 2734 // the HasOneRef() below. This call can't be optimistic and we are checking |
| 2735 // that here. |
| 2736 EXPECT_EQ(net::ERR_IO_PENDING, entry->WriteData( |
| 2737 0, 0, buffer1, kSize1, cb.callback(), false)); |
| 2738 EXPECT_EQ(kSize1, cb.GetResult(net::ERR_IO_PENDING)); |
| 2739 |
| 2740 // Check that we are not leaking. |
| 2741 EXPECT_TRUE( |
| 2742 static_cast<disk_cache::SimpleEntryImpl*>(entry)->HasOneRef()); |
| 2743 entry->Close(); |
| 2744 |
| 2745 // Finish running the pending tasks so that we fully complete the close |
| 2746 // operation and destroy the entry object. |
| 2747 MessageLoop::current()->RunUntilIdle(); |
| 2748 |
| 2749 for (int i = 0; i < disk_cache::kSimpleEntryFileCount; ++i) { |
| 2750 base::FilePath entry_file_path = cache_path_.AppendASCII( |
| 2751 disk_cache::simple_util::GetFilenameFromKeyAndIndex(key, i)); |
| 2752 base::PlatformFileInfo info; |
| 2753 EXPECT_FALSE(file_util::GetFileInfo(entry_file_path, &info)); |
| 2754 } |
| 2755 } |
| 2756 |
2428 // Tests that old entries are evicted while new entries remain in the index. | 2757 // Tests that old entries are evicted while new entries remain in the index. |
2429 // This test relies on non-mandatory properties of the simple Cache Backend: | 2758 // This test relies on non-mandatory properties of the simple Cache Backend: |
2430 // LRU eviction, specific values of high-watermark and low-watermark etc. | 2759 // LRU eviction, specific values of high-watermark and low-watermark etc. |
2431 // When changing the eviction algorithm, the test will have to be re-engineered. | 2760 // When changing the eviction algorithm, the test will have to be re-engineered. |
2432 TEST_F(DiskCacheEntryTest, SimpleCacheEvictOldEntries) { | 2761 TEST_F(DiskCacheEntryTest, SimpleCacheEvictOldEntries) { |
2433 const int kMaxSize = 200 * 1024; | 2762 const int kMaxSize = 200 * 1024; |
2434 const int kWriteSize = kMaxSize / 10; | 2763 const int kWriteSize = kMaxSize / 10; |
2435 const int kNumExtraEntries = 12; | 2764 const int kNumExtraEntries = 12; |
2436 SetSimpleCacheMode(); | 2765 SetSimpleCacheMode(); |
2437 SetMaxSize(kMaxSize); | 2766 SetMaxSize(kMaxSize); |
(...skipping 24 matching lines...) Expand all Loading... |
2462 // is finished. We are testing the positive case, i.e. when the eviction | 2791 // is finished. We are testing the positive case, i.e. when the eviction |
2463 // never reaches this entry, should be non-flaky. | 2792 // never reaches this entry, should be non-flaky. |
2464 ASSERT_EQ(net::OK, OpenEntry(key2 + base::StringPrintf("%d", entry_no), | 2793 ASSERT_EQ(net::OK, OpenEntry(key2 + base::StringPrintf("%d", entry_no), |
2465 &entry)) | 2794 &entry)) |
2466 << "Should not have evicted fresh entry " << entry_no; | 2795 << "Should not have evicted fresh entry " << entry_no; |
2467 entry->Close(); | 2796 entry->Close(); |
2468 } | 2797 } |
2469 } | 2798 } |
2470 | 2799 |
2471 #endif // defined(OS_POSIX) | 2800 #endif // defined(OS_POSIX) |
OLD | NEW |