1 /* 2 * A pipe paging coordinator, permitting memory sharing pipe endpoints. 3 * 4 * Copyright (C) 2021, 2022 Paul Boddie <paul@boddie.org.uk> 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License as 8 * published by the Free Software Foundation; either version 2 of 9 * the License, or (at your option) any later version. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 * 16 * You should have received a copy of the GNU General Public License 17 * along with this program; if not, write to the Free Software 18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, 19 * Boston, MA 02110-1301, USA 20 */ 21 22 #include <algorithm> 23 24 #include <mem/memory_preallocated.h> 25 26 #include "page_queue_partitioned.h" 27 #include "pipe_paging.h" 28 29 30 31 PipePaging::PipePaging(Memory *memory, offset_t size) 32 : NotificationSupport(2), _memory(NULL) 33 { 34 /* Employ a region size compatible with the configured page size. */ 35 36 _size = std::max<offset_t>(size, PAGE_SIZE); 37 38 /* Reserve space for two pipe regions. */ 39 40 _memory = new MemoryPreallocated(memory, round(size, memory->region_size()) * 2); 41 _queue = new PageQueuePartitioned(); 42 _pages = new Pages(_memory, _queue); 43 44 /* Reset the mappers associated with the regions. */ 45 46 for (unsigned int i = 0; i < 2; i++) 47 _regions[i] = NULL; 48 49 /* Initialise a region accessible by reader and writer. */ 50 51 _add_region(); 52 _next_region(); 53 } 54 55 PipePaging::~PipePaging() 56 { 57 /* Discard all regions from the pipe. */ 58 59 for (unsigned int i = 0; i < 2; i++) 60 discard_region(i); 61 62 /* Release notifiers. */ 63 64 release_notifiers(); 65 66 /* Delete the page collection and related objects. */ 67 68 delete _pages; 69 delete _queue; 70 delete _memory; 71 } 72 73 /* Return whether one or more endpoints have detached. */ 74 75 int PipePaging::closed() 76 { 77 return _active_endpoints < 2; 78 } 79 80 /* Detach one endpoint, returning the number still active. */ 81 82 unsigned int PipePaging::detach() 83 { 84 std::lock_guard<std::mutex> guard(_lock); 85 86 if (_active_endpoints) 87 _active_endpoints--; 88 89 return _active_endpoints; 90 } 91 92 /* Discard a region. */ 93 94 void PipePaging::discard_region(unsigned int i) 95 { 96 PageMapper *mapper = _regions[i]; 97 98 if (mapper != NULL) 99 { 100 _regions[i] = NULL; 101 delete mapper; 102 } 103 } 104 105 /* Add a region to the sequence. */ 106 107 PageMapper *PipePaging::_add_region() 108 { 109 /* If the writer already accesses a different region to the reader, no new 110 region is added. */ 111 112 if (_writing != _reading) 113 return NULL; 114 115 /* Select the other region of the pair being maintained. */ 116 117 _writing = 1 - _writing; 118 119 /* Make a new mapper for the region. */ 120 121 PageMapper *mapper = new PageMapper(&_accessors[_writing], _pages); 122 123 /* Initialise and record the mapper. */ 124 125 mapper->set_data_size(0); 126 127 _regions[_writing] = mapper; 128 129 /* Return the next region's mapper. */ 130 131 return mapper; 132 } 133 134 PageMapper *PipePaging::add_region() 135 { 136 PageMapper *mapper = _add_region(); 137 138 /* Let the writer notify the reader. */ 139 140 notify_others(PipePaging::WRITER, NOTIFY_CONTENT_AVAILABLE, NOTIFY_VALUES_NULL); 141 142 /* Return the next region's mapper. */ 143 144 return mapper; 145 } 146 147 /* Return the current region for reading. */ 148 149 PageMapper *PipePaging::current_region() 150 { 151 return _regions[_reading]; 152 } 153 154 /* Return the current region for reading or writing. */ 155 156 PageMapper *PipePaging::current_region(bool writing) 157 { 158 return _regions[writing ? _writing : _reading]; 159 } 160 161 /* Return the next region for the reader if the writer is using a different one. 162 Otherwise, return NULL. */ 163 164 PageMapper *PipePaging::_next_region() 165 { 166 /* If the reader already accesses the same region as the writer, no next 167 region can be obtained. */ 168 169 if (_reading == _writing) 170 return NULL; 171 172 /* Detach and discard the current page mapper. */ 173 174 discard_region(_reading); 175 176 /* Select the next region. */ 177 178 _reading = 1 - _reading; 179 180 /* Return the next region's mapper. */ 181 182 return _regions[_reading]; 183 } 184 185 PageMapper *PipePaging::next_region() 186 { 187 PageMapper *mapper = _next_region(); 188 189 /* Let the reader notify the writer. */ 190 191 notify_others(PipePaging::READER, NOTIFY_SPACE_AVAILABLE, NOTIFY_VALUES_NULL); 192 193 return mapper; 194 } 195 196 // vim: tabstop=4 expandtab shiftwidth=4