1 /* 2 * A pipe paging coordinator, permitting memory sharing pipe endpoints. 3 * 4 * Copyright (C) 2021 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 <mem/memory_preallocated.h> 23 24 #include "page_queue_partitioned.h" 25 #include "pipe_paging.h" 26 27 28 29 PipePaging::PipePaging(Memory *memory, offset_t size) 30 : NotificationSupport(2), _memory(NULL), _size(size) 31 { 32 /* Reserve space for two pipe regions. */ 33 34 _memory = new MemoryPreallocated(memory, round(size, memory->region_size()) * 2); 35 _queue = new PageQueuePartitioned(); 36 _pages = new Pages(_memory, _queue); 37 38 /* Reset the mappers associated with the regions. */ 39 40 for (unsigned int i = 0; i < 2; i++) 41 _regions[i] = NULL; 42 43 /* Initialise a region accessible by reader and writer. */ 44 45 _add_region(); 46 _next_region(); 47 } 48 49 PipePaging::~PipePaging() 50 { 51 /* Discard all regions from the pipe. */ 52 53 for (unsigned int i = 0; i < 2; i++) 54 discard_region(i); 55 56 /* Release notifiers. */ 57 58 release_notifiers(); 59 60 /* Delete the page collection and related objects. */ 61 62 delete _pages; 63 delete _queue; 64 delete _memory; 65 } 66 67 /* Return whether one or more endpoints have detached. */ 68 69 int PipePaging::closed() 70 { 71 return _active_endpoints < 2; 72 } 73 74 /* Detach one endpoint, returning the number still active. */ 75 76 unsigned int PipePaging::detach() 77 { 78 if (_active_endpoints) 79 _active_endpoints--; 80 81 return _active_endpoints; 82 } 83 84 /* Discard a region. */ 85 86 void PipePaging::discard_region(unsigned int i) 87 { 88 PageMapper *mapper = _regions[i]; 89 90 if (mapper != NULL) 91 { 92 mapper->detach(); 93 _regions[i] = NULL; 94 delete mapper; 95 } 96 } 97 98 /* Add a region to the sequence. */ 99 100 PageMapper *PipePaging::_add_region() 101 { 102 /* If the writer already accesses a different region to the reader, no new 103 region is added. */ 104 105 if (_writing != _reading) 106 return NULL; 107 108 /* Select the other region of the pair being maintained. */ 109 110 _writing = 1 - _writing; 111 112 /* Make a new mapper for the region. */ 113 114 PageMapper *mapper = new PageMapper(&_accessors[_writing], _pages); 115 116 /* Initialise and record the mapper. */ 117 118 mapper->attach(); 119 mapper->set_data_size(0); 120 121 _regions[_writing] = mapper; 122 123 /* Return the next region's mapper. */ 124 125 return mapper; 126 } 127 128 PageMapper *PipePaging::add_region() 129 { 130 PageMapper *mapper = _add_region(); 131 132 /* Let the writer notify the reader. */ 133 134 notify_others(PipePaging::WRITER, NOTIFY_CONTENT_AVAILABLE); 135 136 /* Return the next region's mapper. */ 137 138 return mapper; 139 } 140 141 /* Return the current region for reading. */ 142 143 PageMapper *PipePaging::current_region() 144 { 145 return _regions[_reading]; 146 } 147 148 /* Return the current region for reading or writing. */ 149 150 PageMapper *PipePaging::current_region(bool writing) 151 { 152 return _regions[writing ? _writing : _reading]; 153 } 154 155 /* Return the next region for the reader if the writer is using a different one. 156 Otherwise, return NULL. */ 157 158 PageMapper *PipePaging::_next_region() 159 { 160 /* If the reader already accesses the same region as the writer, no next 161 region can be obtained. */ 162 163 if (_reading == _writing) 164 return NULL; 165 166 /* Detach and discard the current page mapper. */ 167 168 discard_region(_reading); 169 170 /* Select the next region. */ 171 172 _reading = 1 - _reading; 173 174 /* Return the next region's mapper. */ 175 176 return _regions[_reading]; 177 } 178 179 PageMapper *PipePaging::next_region() 180 { 181 PageMapper *mapper = _next_region(); 182 183 /* Let the reader notify the writer. */ 184 185 notify_others(PipePaging::READER, NOTIFY_SPACE_AVAILABLE); 186 187 return mapper; 188 } 189 190 // vim: tabstop=4 expandtab shiftwidth=4