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 /* Return whether one or more endpoints have detached. */ 50 51 int PipePaging::closed() 52 { 53 return _active_endpoints < 2; 54 } 55 56 void PipePaging::discard_region(unsigned int i) 57 { 58 PageMapper *mapper = _regions[i]; 59 60 if (mapper != NULL) 61 { 62 mapper->detach(); 63 _regions[i] = NULL; 64 delete mapper; 65 } 66 } 67 68 /* Detach one endpoint. */ 69 70 void PipePaging::detach() 71 { 72 if (!_active_endpoints) 73 return; 74 else 75 _active_endpoints--; 76 77 /* Return if the other endpoint is attached. */ 78 79 if (_active_endpoints) 80 return; 81 82 /* Discard all regions from the pipe. */ 83 84 for (unsigned int i = 0; i < 2; i++) 85 discard_region(i); 86 87 /* Release notifiers. */ 88 89 release_notifiers(); 90 91 /* Delete the page collection and related objects. */ 92 93 delete _pages; 94 delete _queue; 95 delete _memory; 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