1 /*
2  * Copyright (C) 2008 Apple Inc. All Rights Reserved.
3  *
4  * Redistribution and use in source and binary forms, with or without
5  * modification, are permitted provided that the following conditions
6  * are met:
7  * 1. Redistributions of source code must retain the above copyright
8  *    notice, this list of conditions and the following disclaimer.
9  * 2. Redistributions in binary form must reproduce the above copyright
10  *    notice, this list of conditions and the following disclaimer in the
11  *    documentation and/or other materials provided with the distribution.
12  *
13  * THIS SOFTWARE IS PROVIDED BY APPLE INC. ``AS IS'' AND ANY
14  * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL APPLE INC. OR
17  * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
18  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
19  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
20  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
21  * OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
22  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
23  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
24  */
25 
26 #include "config.h"
27 
28 #if ENABLE(PURGEABLE_MEMORY)
29 
30 #include "PurgeableBuffer.h"
31 
32 #include <mach/mach.h>
33 #include <wtf/Assertions.h>
34 #include <wtf/VMTags.h>
35 
36 namespace WebCore {
37 
38 // Purgeable buffers are allocated in multiples of the page size (4KB in common CPUs) so
39 // it does not make sense for very small buffers. Set our minimum size to 16KB.
40 static const size_t minPurgeableBufferSize = 4 * 4096;
41 
PurgeableBuffer(char * data,size_t size)42 PurgeableBuffer::PurgeableBuffer(char* data, size_t size)
43     : m_data(data)
44     , m_size(size)
45     , m_purgePriority(PurgeDefault)
46     , m_state(NonVolatile)
47 {
48 }
49 
~PurgeableBuffer()50 PurgeableBuffer::~PurgeableBuffer()
51 {
52     vm_deallocate(mach_task_self(), reinterpret_cast<vm_address_t>(m_data), m_size);
53 }
54 
create(const char * data,size_t size)55 PassOwnPtr<PurgeableBuffer> PurgeableBuffer::create(const char* data, size_t size)
56 {
57     if (size < minPurgeableBufferSize)
58         return nullptr;
59 
60     vm_address_t buffer = 0;
61     kern_return_t ret = vm_allocate(mach_task_self(), &buffer, size, VM_FLAGS_PURGABLE | VM_FLAGS_ANYWHERE | VM_TAG_FOR_WEBCORE_PURGEABLE_MEMORY);
62 
63     ASSERT(ret == KERN_SUCCESS);
64     if (ret != KERN_SUCCESS)
65         return nullptr;
66 
67     ret = vm_copy(mach_task_self(), reinterpret_cast<vm_address_t>(data), size, buffer);
68 
69     ASSERT(ret == KERN_SUCCESS);
70     if (ret != KERN_SUCCESS) {
71         vm_deallocate(mach_task_self(), buffer, size);
72         return nullptr;
73     }
74 
75     return adoptPtr(new PurgeableBuffer(reinterpret_cast<char*>(buffer), size));
76 }
77 
makePurgeable(bool purgeable)78 bool PurgeableBuffer::makePurgeable(bool purgeable)
79 {
80     if (purgeable) {
81         if (m_state != NonVolatile)
82             return true;
83 
84         int volatileGroup;
85         if (m_purgePriority == PurgeFirst)
86             volatileGroup = VM_VOLATILE_GROUP_0;
87         else if (m_purgePriority == PurgeMiddle)
88             volatileGroup = VM_VOLATILE_GROUP_4;
89         else
90             volatileGroup = VM_VOLATILE_GROUP_7;
91 
92         int state = VM_PURGABLE_VOLATILE | volatileGroup;
93         // So apparently "purgeable" is the correct spelling and the API here is misspelled.
94         kern_return_t ret = vm_purgable_control(mach_task_self(), reinterpret_cast<vm_address_t>(m_data), VM_PURGABLE_SET_STATE, &state);
95 
96         if (ret != KERN_SUCCESS) {
97             // If that failed we have no clue what state we are in so assume purged.
98             m_state = Purged;
99             return true;
100         }
101 
102         m_state = Volatile;
103         return true;
104     }
105 
106     if (m_state == NonVolatile)
107         return true;
108     if (m_state == Purged)
109         return false;
110 
111     int state = VM_PURGABLE_NONVOLATILE;
112     kern_return_t ret = vm_purgable_control(mach_task_self(), reinterpret_cast<vm_address_t>(m_data), VM_PURGABLE_SET_STATE, &state);
113 
114     if (ret != KERN_SUCCESS) {
115         // If that failed we have no clue what state we are in so assume purged.
116         m_state = Purged;
117         return false;
118     }
119 
120     m_state = state & VM_PURGABLE_EMPTY ? Purged : NonVolatile;
121     return m_state == NonVolatile;
122 }
123 
wasPurged() const124 bool PurgeableBuffer::wasPurged() const
125 {
126     if (m_state == NonVolatile)
127         return false;
128     if (m_state == Purged)
129         return true;
130 
131     int state;
132     kern_return_t ret = vm_purgable_control(mach_task_self(), reinterpret_cast<vm_address_t>(m_data), VM_PURGABLE_GET_STATE, &state);
133 
134     if (ret != KERN_SUCCESS) {
135         // If that failed we have no clue what state we are in so assume purged.
136         m_state = Purged;
137         return true;
138     }
139 
140     if (state & VM_PURGABLE_EMPTY) {
141         m_state = Purged;
142         return true;
143     }
144 
145     return false;
146 }
147 
data() const148 const char* PurgeableBuffer::data() const
149 {
150     ASSERT(m_state == NonVolatile);
151     return m_data;
152 }
153 
154 }
155 
156 #endif
157