1 /*
2  * Copyright (c) 2015, 2021, Oracle and/or its affiliates.
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License, version 2.0,
6  * as published by the Free Software Foundation.
7  *
8  * This program is also distributed with certain software (including
9  * but not limited to OpenSSL) that is licensed under separate terms,
10  * as designated in a particular file or component or in included license
11  * documentation.  The authors of MySQL hereby grant you an additional
12  * permission to link the program and your derivative works with the
13  * separately licensed software that they have included with MySQL.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License, version 2.0, for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
23  * 02110-1301  USA
24  */
25 
26 #ifndef _NGS_PAGE_POOL_H_
27 #define _NGS_PAGE_POOL_H_
28 
29 #include <stdint.h>
30 #include <list>
31 
32 #include "ngs/thread.h"
33 #include "ngs/memory.h"
34 #include "ngs_common/atomic.h"
35 
36 #define BUFFER_PAGE_SIZE 4096
37 
38 
39 namespace ngs
40 {
41 
42   class Page_pool;
43 
44   // 4KB aligned buffer to be used for reading data from sockets.
45   class Page
46   {
47   public:
Page(uint32_t pcapacity,char * pdata)48     Page(uint32_t pcapacity, char *pdata)
49     {
50       capacity = pcapacity;
51       data = pdata;
52       length = 0;
53       references = 0;
54       saved_length = 0;
55     }
56 
57     Page(uint32_t pcapacity = BUFFER_PAGE_SIZE)
58     {
59       capacity = pcapacity;
60       ngs::allocate_array(data, capacity, KEY_memory_x_recv_buffer);
61       length = 0;
62       references = 0;
63       saved_length = 0;
64     }
65 
~Page()66     virtual ~Page() { ngs::free_array(data); }
67 
aquire()68     void aquire() {  ++references; }
release()69     void release() { if (0 == --references) destroy(); }
70 
save_state()71     void save_state() { saved_length = length; }
rollback()72     void rollback() { length = saved_length; }
73 
get_free_bytes()74     uint32_t get_free_bytes() { return capacity - length; }
75 
get_free_ptr()76     uint8_t* get_free_ptr() { return (uint8_t*)data + length; }
77 
78     char *data;
79     uint32_t capacity;
80     uint32_t length;
81 
82   protected:
destroy()83     virtual void destroy() { }
84 
85   private:
86     Page(const Page&);
87     void operator=(const Page&);
88 
89     uint16_t references;
90     uint32_t saved_length;
91   };
92 
93   template<typename ResType>
94   class Resource
95   {
96   public:
97     Resource();
98     Resource(ResType *res);
99     Resource(const Resource<ResType> &resource);
100 
101     ~Resource();
102 
103     ResType *operator->();
104     ResType *operator->() const;
105 
106   private:
107     ResType *m_res;
108   };
109 
110   struct Pool_config
111   {
112     int32_t pages_max;
113     int32_t pages_cache_max;
114     int32_t page_size;
115   };
116 
117 
118   class Page_pool
119   {
120    public:
121     /* Unlimited allocation, no caching */
122     Page_pool(const int32_t page_size = BUFFER_PAGE_SIZE);
123     Page_pool(const Pool_config &pool_config);
124     ~Page_pool();
125 
126     Resource<Page> allocate();
127 
128     class No_more_pages_exception: public std::exception
129     {
130     public:
what()131       virtual const char* what() const throw() { return "No more memory pages available"; }
132     };
133 
134   private:
135     Page_pool(const Page_pool &);
136     Page_pool &operator=(const Page_pool &);
137 
138     class Page_memory_managed : public Page
139     {
140     public:
Page_memory_managed(Page_pool & pool,uint32_t pcapacity,char * pdata)141       Page_memory_managed(Page_pool &pool, uint32_t pcapacity, char *pdata)
142       : Page(pcapacity, pdata), m_pool(pool)
143       { }
144 
~Page_memory_managed()145       ~Page_memory_managed() { data = NULL; }
146 
147     private:
destroy()148       virtual void destroy()
149       {
150         m_pool.deallocate(this);
151       }
152 
153       Page_pool &m_pool;
154     };
155 
156     void deallocate(Page *page);
157 
158     bool push_page(char *page_data);
159     char *pop_page();
160 
161     std::list<char *> m_pages_list;
162     int32_t       m_pages_max;
163     int32_t       m_pages_cache_max;
164     int32_t       m_pages_cached;
165     const int32_t m_page_size;
166     Mutex         m_mutex;
167     ngs::atomic<int32_t> m_pages_allocated;
168   };
169 
170 
171   template<typename ResType>
Resource()172   Resource<ResType>::Resource()
173   : m_res(NULL)
174   {
175   }
176 
177   template<typename ResType>
Resource(ResType * res)178   Resource<ResType>::Resource(ResType *res)
179   : m_res(res)
180   {
181     m_res->aquire();
182   }
183 
184   template<typename ResType>
Resource(const Resource<ResType> & resource)185   Resource<ResType>::Resource(const Resource<ResType> &resource)
186   : m_res(resource.m_res)
187   {
188     if (NULL != m_res)
189       m_res->aquire();
190   }
191 
192   template<typename ResType>
~Resource()193   Resource<ResType>::~Resource()
194   {
195     if (NULL != m_res)
196       m_res->release();
197   }
198 
199   template<typename ResType>
200   ResType *Resource<ResType>::operator->()
201   {
202     return m_res;
203   }
204 
205   template<typename ResType>
206   ResType *Resource<ResType>::operator->() const
207   {
208     return m_res;
209   }
210 } // namespace ngs
211 
212 #endif // _NGS_PAGE_POOL_H_
213