├── .gitignore
├── CMakeLists.txt
├── LICENSE
├── README.md
├── img
├── 1.png
├── 2.png
└── 3.png
├── include
├── Acceptor.h
├── AsyncLogging.h
├── Buffer.h
├── Callbacks.h
├── Channel.h
├── ConsistenHash.h
├── CurrentThread.h
├── EPollPoller.h
├── EventLoop.h
├── EventLoopThread.h
├── EventLoopThreadPool.h
├── FileUtil.h
├── FixedBuffer.h
├── InetAddress.h
├── LFU.h
├── LogFile.h
├── LogStream.h
├── Logger.h
├── Poller.h
├── RICachePolicy.h
├── Socket.h
├── TcpConnection.h
├── TcpServer.h
├── Thread.h
├── Timer.h
├── TimerQueue.h
├── Timestamp.h
├── memoryPool.h
└── noncopyable.h
├── lib
├── liblog_lib.so
├── libmemory_lib.so
└── libsrc_lib.so
├── log
├── AsyncLogging.cc
├── CMakeLists.txt
├── CurrentThread.cc
├── FileUtil.cc
├── LogFile.cc
└── LogStream.cc
├── memory
├── CMakeLists.txt
└── memoryPool.cc
└── src
├── Acceptor.cc
├── Buffer.cc
├── CMakeLists.txt
├── Channel.cc
├── CurrentThread.cc
├── DefaultPoller.cc
├── EPollPoller.cc
├── EventLoop.cc
├── EventLoopThread.cc
├── EventLoopThreadPool.cc
├── InetAddress.cc
├── Logger.cc
├── Poller.cc
├── Socket.cc
├── TcpConnection.cc
├── TcpServer.cc
├── Thread.cc
├── Timer.cc
├── TimerQueue.cc
├── Timestamp.cc
└── main.cc
/.gitignore:
--------------------------------------------------------------------------------
1 | build/
2 | bin/
3 | *.o
4 | *.out
5 | *.log
6 | .vscode/
--------------------------------------------------------------------------------
/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Top-level CMake, set global link libraries, add subdirectories, and project header file locations
2 | # Set minimum version and project name
3 | cmake_minimum_required(VERSION 3.0)
4 | project(ronald-webserver)
5 |
6 | # Set global C++ standard
7 | set (CMAKE_CXX_STANDARD 11)
8 | set (CMAKE_CXX_STANDARD_REQUIRED True)
9 |
10 | # Set header file directory for all subprojects
11 | include_directories(${CMAKE_SOURCE_DIR}/include)
12 |
13 | # Set output directories
14 | set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/bin)
15 | set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/lib)
16 |
17 | # Set global link libraries
18 | set(LIBS
19 | pthread
20 | )
21 |
22 | # Add subdirectories
23 | add_subdirectory(src)
24 | add_subdirectory(memory)
25 | add_subdirectory(log)
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # ronald-webserver
2 |
3 | Project Introduction
4 |
5 | This project is a high-performance WEB server implemented in C++. The project's underlying architecture adopts the core design philosophy of the muduo library, featuring a multi-threaded multi-Reactor network model. Additionally, it incorporates a memory pool, an efficient double-buffered asynchronous logging system, and LFU caching.
6 |
7 | ## Development Environment
8 |
9 | * linux kernel version5.15.0-113-generic (ubuntu 22.04.6)
10 | * gcc (Ubuntu 11.4.0-1ubuntu1~22.04) 11.4.0
11 | * cmake version 3.22
12 |
13 | ## Directory Structure
14 |
15 | ```shell
16 | ronald-webserver/
17 | ├── img/ # Directory for images
18 | ├── include/ # Location for all header files (.h)
19 | ├── lib/ # Directory for shared libraries
20 | |
21 | ├── log/ # Logging management module
22 | │ ├── log.cc # Logging implementation
23 | ├── memory/ # Memory management module
24 | │ ├── memory.cc # Memory management implementation
25 | ├── src/ # Source code directory
26 | │ ├── main.cpp # Main program entry
27 | │ ├── ... # Other source files
28 | |
29 | ├── CMakeLists.txt # CMake build file
30 | ├── LICENSE # License file
31 | └── README.md # Project documentation
32 | ```
33 |
34 | ## Prerequisites
35 |
36 | Install basic tools
37 |
38 | ```bash
39 | sudo apt-get update
40 | sudo apt-get install -y wget cmake build-essential unzip git
41 | ```
42 |
43 | ## Compilation Instructions
44 |
45 | 1. Clone the project:
46 |
47 | ```bash
48 | git clone https://github.com/Trapped-Bird/Ronald-webserver.git
49 | cd Ronald-webserver
50 | ```
51 |
52 | 2. Create build directory and compile:
53 |
54 | ```bash
55 | mkdir build &&
56 | cd build &&
57 | cmake .. &&
58 | make -j ${nproc}
59 | ```
60 |
61 | 3. After building, enter the bin directory:
62 |
63 | ```bash
64 | cd bin
65 | ```
66 |
67 | 4. Start the main executable:
68 |
69 | ```bash
70 | ./main
71 | ```
72 |
73 | **Note**: You need to open a new window and run `nc 127.0.0.1 8080` to start our client and connect to the web server started by the main executable.
74 |
75 | ## Running Results
76 |
77 | By running the main executable in the bin directory, you will see the following results:
78 |
79 | The log files will be stored in the `logs` directory under the bin directory. Each time the program runs, a new log file will be generated to record the program's running status and error information.
80 |
81 | - Server running results as shown:
82 |
83 | 
84 |
85 | - Client running results as shown:
86 |
87 | 
88 |
89 | **Note**: The test results still use the echo server test, focusing on the implementation of the architecture.
90 |
91 | ---
92 |
93 | ### Simple Analysis of Log Core Content:
94 |
95 | First, the log results are shown as:
96 | 
97 |
98 | 1. File Descriptor Statistics
99 |
100 | ```bash
101 | 2025/05/13 13:44:522120 INFO fd total count:3 - EPollPoller.cc:32
102 | ```
103 |
104 | - Description: After a poll or event processing, print the number of monitored FDs in the current epoll - 3.
105 | - Usually includes:
106 | Listen socket (listen_fd)
107 | One or more connection socket(client_fd)
108 | Internal pipeline, wake-up fd (for example, for eventfd, timerfd, pipe, etc.)
109 |
110 | 2. Event Triggering
111 |
112 | ```bash
113 | 2025/05/13 13:44:185295 INFO events happend1 - EPollPoller.cc:40
114 | 2025/05/13 13:44:185343 INFO channel handleEvent revents:1 - Channel.cc:73
115 | ```
116 |
117 | - An event occurred (events happend1), possibly a client socket close event.
118 | - revents:1 indicates the triggered event type is EPOLLIN, meaning the peer closed the connection or sent data.
119 |
120 | 3. Connection Close Handling
121 |
122 | ```bash
123 | 2025/05/13 13:44:185377 INFO TcpConnection::handleClose fd=13state=2 - TcpConnection.cc:241
124 | 2025/05/13 13:44:185390 INFO func =>fd13events=0index=1 - EPollPoller.cc:66
125 | 2025/05/13 13:44:185404 INFO Connection DOWN :127.0.0.1:33174 - main.cc:44
126 | ```
127 |
128 | - TcpConnection::handleClose: Connection with file descriptor fd=13 closed, current state state=2 (possibly indicating "connected" state).
129 | - Connection DOWN: Connection with client 127.0.0.1:33174 disconnected.
130 | - events=0: Indicates this file descriptor is no longer listening for any events.
131 |
132 | 4. Removing Connection from Server
133 |
134 | ```bash
135 | 2025/05/13 13:44:185476 INFO TcpServer::removeConnectionInLoop [EchoServer] - connection %sEchoServer-127.0.0.1:8080#1 - TcpServer.cc:114
136 | 2025/05/13 13:44:185618 INFO removeChannel fd=13 - EPollPoller.cc:102
137 | ```
138 |
139 | - TcpServer::removeConnectionInLoop: Server internally removes binding with connection 127.0.0.1:47376.
140 | - removeChannel: Removed file descriptor fd=13 from EPoll's event listening list.
141 |
142 | 5. Resource Cleanup
143 |
144 | ```bash
145 | 2025/05/13 13:44:185631 INFO TcpConnection::dtor[EchoServer-127.0.0.1:8080#1]at fd=13state=0 - TcpConnection.cc:58
146 | ```
147 |
148 | - TcpConnection destructor (dtor) called, releasing connection-related resources.
149 | - State state=0 indicates connection is completely closed, file descriptor fd=13 is destroyed.
150 |
151 | ## Functional Module Division
152 |
153 | ### Network Module
154 |
155 | - **Event Polling and Distribution Module**: `EventLoop.*`, `Channel.*`, `Poller.*`, `EPollPoller.*` responsible for event polling detection and implementing event distribution processing. `EventLoop` polls `Poller`, and `Poller` is implemented by `EPollPoller`.
156 | - **Thread and Event Binding Module**: `Thread.*`, `EventLoopThread.*`, `EventLoopThreadPool.*` bind threads with event loops, completing the `one loop per thread` model.
157 | - **Network Connection Module**: `TcpServer.*`, `TcpConnection.*`, `Acceptor.*`, `Socket.*` implement `mainloop` response to network connections and distribute to various `subloop`s.
158 | - **Buffer Module**: `Buffer.*` provides auto-expanding buffer to ensure ordered data arrival.
159 |
160 | ### Logging Module
161 |
162 | - The logging module is responsible for recording important information during server operation, helping developers with debugging and performance analysis. Log files are stored in the `bin/logs/` directory.
163 |
164 | ### Memory Management
165 |
166 | - The memory management module is responsible for dynamic memory allocation and deallocation, ensuring server stability and performance under high load conditions.
167 |
168 | ### LFU Cache Module
169 |
170 | - Used to decide which content to delete to free up space when cache capacity is insufficient. The core idea of LFU is to prioritize removing the least frequently used cache items.
171 |
--------------------------------------------------------------------------------
/img/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronald-debugging/Ronald-webserver/5bc27cb51466a32851dc6888e7d354c60cf09206/img/1.png
--------------------------------------------------------------------------------
/img/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronald-debugging/Ronald-webserver/5bc27cb51466a32851dc6888e7d354c60cf09206/img/2.png
--------------------------------------------------------------------------------
/img/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronald-debugging/Ronald-webserver/5bc27cb51466a32851dc6888e7d354c60cf09206/img/3.png
--------------------------------------------------------------------------------
/include/Acceptor.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 |
5 | #include "noncopyable.h"
6 | #include "Socket.h"
7 | #include "Channel.h"
8 |
9 | class EventLoop;
10 | class InetAddress;
11 |
12 | class Acceptor : noncopyable
13 | {
14 | public:
15 | using NewConnectionCallback = std::function;
16 |
17 | Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport);
18 | ~Acceptor();
19 | // Set the callback function for new connections
20 | void setNewConnectionCallback(const NewConnectionCallback &cb) { NewConnectionCallback_ = cb; }
21 | // Check if listening
22 | bool listenning() const { return listenning_; }
23 | // Listen on local port
24 | void listen();
25 |
26 | private:
27 | void handleRead(); // Handle new user connection event
28 |
29 | EventLoop *loop_; // The baseLoop defined by the user, also called mainLoop
30 | Socket acceptSocket_; // Dedicated socket for receiving new connections
31 | Channel acceptChannel_; // Dedicated channel for listening to new connections
32 | NewConnectionCallback NewConnectionCallback_; // Callback function for new connections
33 | bool listenning_; // Whether it is listening
34 | };
--------------------------------------------------------------------------------
/include/AsyncLogging.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "noncopyable.h"
4 | #include "Thread.h"
5 | #include "FixedBuffer.h"
6 | #include "LogStream.h"
7 | #include "LogFile.h"
8 |
9 | #include
10 | #include
11 | #include
12 | #include
13 |
14 | class AsyncLogging
15 | {
16 | public:
17 | AsyncLogging(const std::string &basename, off_t rollSize, int flushInterval=3);
18 | ~AsyncLogging()
19 | {
20 | if (running_)
21 | {
22 | stop();
23 | }
24 | }
25 | // Frontend calls append to write logs
26 | void append(const char *logline, int len);
27 | void start()
28 | {
29 | running_ = true;
30 | thread_.start();
31 | }
32 | void stop()
33 | {
34 | running_ = false;
35 | cond_.notify_one();
36 | }
37 |
38 | private:
39 | using LargeBuffer = FixedBuffer;
40 | using BufferVector = std::vector>;
41 | // BufferVector::value_type is the element type of std::vector>, which is std::unique_ptr.
42 | using BufferPtr = BufferVector::value_type;
43 | void threadFunc();
44 | const int flushInterval_; // Log flush interval
45 | std::atomic running_;
46 | const std::string basename_;
47 | const off_t rollSize_;
48 | Thread thread_;
49 | std::mutex mutex_;
50 | std::condition_variable cond_;
51 |
52 | BufferPtr currentBuffer_;
53 | BufferPtr nextBuffer_;
54 | BufferVector buffers_;
55 | };
--------------------------------------------------------------------------------
/include/Buffer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | // Definition of the underlying buffer type for the network library
9 | class Buffer
10 | {
11 | public:
12 | static const size_t kCheapPrepend = 8; // Initial reserved prependable space size
13 | static const size_t kInitialSize = 1024;
14 |
15 | explicit Buffer(size_t initalSize = kInitialSize)
16 | : buffer_(kCheapPrepend + initalSize)
17 | , readerIndex_(kCheapPrepend)
18 | , writerIndex_(kCheapPrepend)
19 | {
20 | }
21 |
22 | size_t readableBytes() const { return writerIndex_ - readerIndex_; }
23 | size_t writableBytes() const { return buffer_.size() - writerIndex_; }
24 | size_t prependableBytes() const { return readerIndex_; }
25 |
26 | // Return the starting address of readable data in the buffer
27 | const char *peek() const { return begin() + readerIndex_; }
28 | void retrieve(size_t len)
29 | {
30 | if (len < readableBytes())
31 | {
32 | readerIndex_ += len; // Indicates that the application only read part of the readable buffer data, which is of length len. The data from readerIndex_ + len to writerIndex_ is not read yet
33 | }
34 | else // len == readableBytes()
35 | {
36 | retrieveAll();
37 | }
38 | }
39 | void retrieveAll()
40 | {
41 | readerIndex_ = kCheapPrepend;
42 | writerIndex_ = kCheapPrepend;
43 | }
44 |
45 | // Convert the Buffer data reported by onMessage function to string type and return
46 | std::string retrieveAllAsString() { return retrieveAsString(readableBytes()); }
47 | std::string retrieveAsString(size_t len)
48 | {
49 | std::string result(peek(), len);
50 | retrieve(len); // The above line has already read the readable data from the buffer, so the buffer must be reset here
51 | return result;
52 | }
53 |
54 | // buffer_.size - writerIndex_
55 | void ensureWritableBytes(size_t len)
56 | {
57 | if (writableBytes() < len)
58 | {
59 | makeSpace(len); // Expand
60 | }
61 | }
62 |
63 | // Add data in memory [data, data+len] to the writable buffer
64 | void append(const char *data, size_t len)
65 | {
66 | ensureWritableBytes(len);
67 | std::copy(data, data+len, beginWrite());
68 | writerIndex_ += len;
69 | }
70 | char *beginWrite() { return begin() + writerIndex_; }
71 | const char *beginWrite() const { return begin() + writerIndex_; }
72 |
73 | // Read data from fd
74 | ssize_t readFd(int fd, int *saveErrno);
75 | // Send data through fd
76 | ssize_t writeFd(int fd, int *saveErrno);
77 |
78 | private:
79 | // The address of the first element of the underlying array of vector, which is the starting address of the array
80 | char *begin() { return &*buffer_.begin(); }
81 | const char *begin() const { return &*buffer_.begin(); }
82 |
83 | void makeSpace(size_t len)
84 | {
85 | /**
86 | * | kCheapPrepend |xxx| reader | writer | // xxx indicates the read part in reader
87 | * | kCheapPrepend | reader | len |
88 | **/
89 | if (writableBytes() + prependableBytes() < len + kCheapPrepend) // That is, len > remaining space before xxx + writer part
90 | {
91 | buffer_.resize(writerIndex_ + len);
92 | }
93 | else // Here len <= xxx + writer, move reader to start from xxx to make continuous space after xxx
94 | {
95 | size_t readable = readableBytes(); // readable = length of reader
96 | // Copy the data from readerIndex_ to writerIndex_ in the current buffer
97 | // to the position kCheapPrepend at the beginning of the buffer, so as to make more writable space
98 | std::copy(begin() + readerIndex_,
99 | begin() + writerIndex_,
100 | begin() + kCheapPrepend);
101 | readerIndex_ = kCheapPrepend;
102 | writerIndex_ = readerIndex_ + readable;
103 | }
104 | }
105 |
106 | std::vector buffer_;
107 | size_t readerIndex_;
108 | size_t writerIndex_;
109 | };
110 |
--------------------------------------------------------------------------------
/include/Callbacks.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | class Buffer;
7 | class TcpConnection;
8 | class Timestamp;
9 |
10 | using TcpConnectionPtr = std::shared_ptr;
11 | using ConnectionCallback = std::function;
12 | using CloseCallback = std::function;
13 | using WriteCompleteCallback = std::function;
14 | using HighWaterMarkCallback = std::function;
15 |
16 | using MessageCallback = std::function;
--------------------------------------------------------------------------------
/include/Channel.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "noncopyable.h"
7 | #include "Timestamp.h"
8 |
9 | class EventLoop;
10 |
11 | /**
12 | * Clarify the relationship between EventLoop, Channel, and Poller. In the Reactor model, they correspond to the event dispatcher.
13 | * Channel can be understood as a conduit that encapsulates sockfd and its interested events such as EPOLLIN, EPOLLOUT, and also binds the specific events returned by poller.
14 | **/
15 | class Channel : noncopyable
16 | {
17 | public:
18 | using EventCallback = std::function; // muduo still uses typedef
19 | using ReadEventCallback = std::function;
20 |
21 | Channel(EventLoop *loop, int fd);
22 | ~Channel();
23 |
24 | // After fd is notified by Poller, handle the event. handleEvent is called in EventLoop::loop()
25 | void handleEvent(Timestamp receiveTime);
26 |
27 | // Set callback function object
28 | void setReadCallback(ReadEventCallback cb) { readCallback_ = std::move(cb); }
29 | void setWriteCallback(EventCallback cb) { writeCallback_ = std::move(cb); }
30 | void setCloseCallback(EventCallback cb) { closeCallback_ = std::move(cb); }
31 | void setErrorCallback(EventCallback cb) { errorCallback_ = std::move(cb); }
32 |
33 | // Prevent the channel from being manually removed while still executing callback operations
34 | void tie(const std::shared_ptr &);
35 |
36 | int fd() const { return fd_; }
37 | int events() const { return events_; }
38 | void set_revents(int revt) { revents_ = revt; }
39 |
40 | // Set the event status of fd, equivalent to epoll_ctl add/delete
41 | void enableReading() { events_ |= kReadEvent; update(); }
42 | void disableReading() { events_ &= ~kReadEvent; update(); }
43 | void enableWriting() { events_ |= kWriteEvent; update(); }
44 | void disableWriting() { events_ &= ~kWriteEvent; update(); }
45 | void disableAll() { events_ = kNoneEvent; update(); }
46 |
47 | // Return the current event status of fd
48 | bool isNoneEvent() const { return events_ == kNoneEvent; }
49 | bool isWriting() const { return events_ & kWriteEvent; }
50 | bool isReading() const { return events_ & kReadEvent; }
51 |
52 | int index() { return index_; }
53 | void set_index(int idx) { index_ = idx; }
54 |
55 | // one loop per thread
56 | EventLoop *ownerLoop() { return loop_; }
57 | void remove();
58 | private:
59 |
60 | void update();
61 | void handleEventWithGuard(Timestamp receiveTime);
62 |
63 | static const int kNoneEvent;
64 | static const int kReadEvent;
65 | static const int kWriteEvent;
66 |
67 | EventLoop *loop_; // Event loop
68 | const int fd_; // fd, the object Poller listens to
69 | int events_; // Registered events of interest for fd
70 | int revents_; // Specific events returned by Poller
71 | int index_;
72 |
73 | std::weak_ptr tie_;
74 | bool tied_;
75 |
76 | // Since the channel can know the specific events that occurred on fd, it is responsible for calling the corresponding event callback operations
77 | ReadEventCallback readCallback_;
78 | EventCallback writeCallback_;
79 | EventCallback closeCallback_;
80 | EventCallback errorCallback_;
81 | };
--------------------------------------------------------------------------------
/include/ConsistenHash.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | /**
11 | * @class ConsistentHash
12 | * @brief Class implementing the consistent hashing algorithm.
13 | *
14 | * Consistent hashing is a distributed hashing technique designed to minimize key redistribution when nodes are added or removed.
15 | * Commonly used in distributed cache systems and distributed database sharding.
16 | */
17 | class ConsistentHash {
18 | public:
19 | /**
20 | * @brief Constructor
21 | * @param numReplicas Number of virtual nodes per physical node. Increasing virtual nodes can improve load balancing.
22 | * @param hashFunc Optional custom hash function, defaults to std::hash.
23 | */
24 | ConsistentHash(size_t numReplicas, std::function hashFunc = std::hash())
25 | : numReplicas_(numReplicas), hashFunction_(hashFunc) {}
26 |
27 | /**
28 | * @brief Add a node to the hash ring.
29 | *
30 | * Each node is replicated into several virtual nodes. Each virtual node calculates a unique hash value using `node + index`.
31 | * These hash values are stored on the hash ring and sorted for efficient lookup.
32 | *
33 | * @param node Name of the node to add (e.g., server address).
34 | */
35 | void addNode(const std::string& node) {
36 | std::lock_guard lock(mtx_); // Ensure thread safety
37 | for (size_t i = 0; i < numReplicas_; ++i) {
38 | // Calculate a unique hash value for each virtual node
39 | size_t hash = hashFunction_(node +"_0"+std::to_string(i));
40 | circle_[hash] = node; // Hash value maps to node
41 | sortedHashes_.push_back(hash); // Add to sorted list
42 | }
43 | // Sort the hash values
44 | std::sort(sortedHashes_.begin(), sortedHashes_.end());
45 | }
46 |
47 | /**
48 | * @brief Remove a node from the hash ring.
49 | *
50 | * Delete all virtual nodes of the node and their corresponding hash values.
51 | *
52 | * @param node Name of the node to remove.
53 | */
54 | void removeNode(const std::string& node) {
55 | std::lock_guard lock(mtx_); // Ensure thread safety
56 | for (size_t i = 0; i < numReplicas_; ++i) {
57 | // Calculate the hash value of the virtual node
58 | size_t hash = hashFunction_(node + std::to_string(i));
59 | circle_.erase(hash); // Remove the hash from the hash ring
60 | auto it = std::find(sortedHashes_.begin(), sortedHashes_.end(), hash);
61 | if (it != sortedHashes_.end()) {
62 | sortedHashes_.erase(it); // Remove from sorted list
63 | }
64 | }
65 | }
66 |
67 | /**
68 | * @brief Find the node responsible for handling the given key.
69 | *
70 | * Find the first node in the hash ring whose hash value is greater than or equal to the key's hash value.
71 | * If not found (i.e., exceeds the maximum value of the hash ring), wrap around to the first node.
72 | *
73 | * @param key The key to look up (e.g., data identifier).
74 | * @return The name of the node responsible for the key.
75 | * @throws std::runtime_error If the hash ring is empty (no nodes).
76 | */
77 | size_t getNode(const std::string& key) {
78 | std::lock_guard lock(mtx_); // Ensure thread safety
79 | if (circle_.empty()) {
80 | throw std::runtime_error("No nodes in consistent hash"); // Throw exception if ring is empty
81 | }
82 | size_t hash = hashFunction_(key); // Calculate the hash value of the key
83 | // Find the first position in the sorted hash list greater than the key's hash value
84 | auto it = std::upper_bound(sortedHashes_.begin(), sortedHashes_.end(), hash);
85 | if (it == sortedHashes_.end()) {
86 | // If it exceeds the maximum value of the ring, wrap around to the first node
87 | it = sortedHashes_.begin();
88 | }
89 | return *it; // Return the corresponding hash value
90 | }
91 |
92 | private:
93 | size_t numReplicas_; // Number of virtual nodes per physical node
94 | std::function hashFunction_; // User-defined or default hash function
95 | std::unordered_map circle_; // Mapping from hash value to node name
96 | std::vector sortedHashes_; // Sorted list of hash values for efficient lookup
97 | std::mutex mtx_; // Mutex to protect the hash ring and ensure thread safety
98 | };
99 |
--------------------------------------------------------------------------------
/include/CurrentThread.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | namespace CurrentThread
6 | {
7 | extern thread_local int t_cachedTid; // Cache tid because system calls are very time-consuming, save tid after getting it
8 |
9 | void cacheTid();
10 |
11 | inline int tid() // Inline function only works in current file
12 | {
13 | if (__builtin_expect(t_cachedTid == 0, 0)) // __builtin_expect is a low-level optimization, this statement means if tid hasn't been obtained yet, enter if and get tid through cacheTid() system call
14 | {
15 | cacheTid();
16 | }
17 | return t_cachedTid;
18 | }
19 | }
--------------------------------------------------------------------------------
/include/EPollPoller.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "Poller.h"
7 | #include "Timestamp.h"
8 |
9 | /**
10 | * Usage of epoll:
11 | * 1. epoll_create
12 | * 2. epoll_ctl (add, mod, del)
13 | * 3. epoll_wait
14 | **/
15 |
16 | class Channel;
17 |
18 | class EPollPoller : public Poller
19 | {
20 | public:
21 | EPollPoller(EventLoop *loop);
22 | ~EPollPoller() override;
23 |
24 | // Override the abstract method of base class Poller
25 | Timestamp poll(int timeoutMs, ChannelList *activeChannels) override;
26 | void updateChannel(Channel *channel) override;
27 | void removeChannel(Channel *channel) override;
28 |
29 | private:
30 | static const int kInitEventListSize = 16;
31 |
32 | // Fill active connections
33 | void fillActiveChannels(int numEvents, ChannelList *activeChannels) const;
34 | // Update channel, actually calls epoll_ctl
35 | void update(int operation, Channel *channel);
36 |
37 | using EventList = std::vector; // In C++, you can omit struct and just write epoll_event
38 |
39 | int epollfd_; // The fd returned by epoll_create is saved in epollfd_
40 | EventList events_; // Used to store all file descriptor event sets returned by epoll_wait
41 | };
--------------------------------------------------------------------------------
/include/EventLoop.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | #include "noncopyable.h"
10 | #include "Timestamp.h"
11 | #include "CurrentThread.h"
12 | #include "TimerQueue.h"
13 | class Channel;
14 | class Poller;
15 |
16 | // Event loop class, mainly contains two major modules: Channel and Poller (epoll abstraction)
17 | class EventLoop : noncopyable
18 | {
19 | public:
20 | using Functor = std::function;
21 |
22 | EventLoop();
23 | ~EventLoop();
24 |
25 | // Start event loop
26 | void loop();
27 | // Exit event loop
28 | void quit();
29 |
30 | Timestamp pollReturnTime() const { return pollRetureTime_; }
31 |
32 | // Execute in the current loop
33 | void runInLoop(Functor cb);
34 | // Put the upper-level registered callback function cb into the queue and wake up the thread where the loop is located to execute cb
35 | void queueInLoop(Functor cb);
36 |
37 | // Wake up the thread where the loop is located through eventfd
38 | void wakeup();
39 |
40 | // EventLoop's methods => Poller's methods
41 | void updateChannel(Channel *channel);
42 | void removeChannel(Channel *channel);
43 | bool hasChannel(Channel *channel);
44 |
45 | // Determine whether the EventLoop object is in its own thread
46 | bool isInLoopThread() const { return threadId_ == CurrentThread::tid(); } // threadId_ is the thread id when EventLoop is created, CurrentThread::tid() is the current thread id
47 | /**
48 | * Timer task related functions
49 | */
50 | void runAt(Timestamp timestamp, Functor &&cb)
51 | {
52 | timerQueue_->addTimer(std::move(cb), timestamp, 0.0);
53 | }
54 |
55 | void runAfter(double waitTime, Functor &&cb)
56 | {
57 | Timestamp time(addTime(Timestamp::now(), waitTime));
58 | runAt(time, std::move(cb));
59 | }
60 |
61 | void runEvery(double interval, Functor &&cb)
62 | {
63 | Timestamp timestamp(addTime(Timestamp::now(), interval));
64 | timerQueue_->addTimer(std::move(cb), timestamp, interval);
65 | }
66 |
67 | private:
68 | void handleRead(); // Event callback bound to the file descriptor wakeupFd_ returned by eventfd. When wakeup() is called, i.e., when an event occurs, handleRead() reads 8 bytes from wakeupFd_ and wakes up the blocked epoll_wait
69 | void doPendingFunctors(); // Execute upper-level callbacks
70 |
71 | using ChannelList = std::vector;
72 |
73 | std::atomic_bool looping_; // Atomic operation, implemented by CAS at the bottom
74 | std::atomic_bool quit_; // Flag to exit loop
75 |
76 | const pid_t threadId_; // Record which thread id created the current EventLoop, i.e., identifies the thread id to which the current EventLoop belongs
77 |
78 | Timestamp pollRetureTime_; // The time when Poller returns the Channels where events occurred
79 | std::unique_ptr poller_;
80 | std::unique_ptr timerQueue_;
81 | int wakeupFd_; // Function: When mainLoop gets a new user's Channel, it needs to select a subLoop through polling algorithm and wake up subLoop to process the Channel through this member
82 | std::unique_ptr wakeupChannel_;
83 |
84 | ChannelList activeChannels_; // Return the list of all Channels where events are currently detected by Poller
85 |
86 | std::atomic_bool callingPendingFunctors_; // Indicates whether the current loop has callback operations to execute
87 | std::vector pendingFunctors_; // Store all callback operations that the loop needs to execute
88 | std::mutex mutex_; // Mutex to protect thread-safe operations on the above vector container
89 | };
--------------------------------------------------------------------------------
/include/EventLoopThread.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "noncopyable.h"
9 | #include "Thread.h"
10 |
11 | class EventLoop;
12 |
13 | class EventLoopThread : noncopyable
14 | {
15 | public:
16 | using ThreadInitCallback = std::function;
17 |
18 | EventLoopThread(const ThreadInitCallback &cb = ThreadInitCallback(),
19 | const std::string &name = std::string());
20 | ~EventLoopThread();
21 |
22 | EventLoop *startLoop();
23 |
24 | private:
25 | void threadFunc();
26 |
27 | EventLoop *loop_;
28 | bool exiting_;
29 | Thread thread_;
30 | std::mutex mutex_; // Mutex
31 | std::condition_variable cond_; // Condition variable
32 | ThreadInitCallback callback_;
33 | };
--------------------------------------------------------------------------------
/include/EventLoopThreadPool.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | #include "noncopyable.h"
9 | #include "ConsistenHash.h"
10 | class EventLoop;
11 | class EventLoopThread;
12 |
13 | class EventLoopThreadPool : noncopyable
14 | {
15 | public:
16 | using ThreadInitCallback = std::function;
17 |
18 | EventLoopThreadPool(EventLoop *baseLoop, const std::string &nameArg);
19 | ~EventLoopThreadPool();
20 |
21 | void setThreadNum(int numThreads) { numThreads_ = numThreads; }
22 |
23 | void start(const ThreadInitCallback &cb = ThreadInitCallback());
24 |
25 | // If working in multithreading, baseLoop_ (mainLoop) will assign Channel to subLoop in a round-robin way by default
26 | EventLoop *getNextLoop(const std::string& key);
27 |
28 | std::vector getAllLoops(); // Get all EventLoops
29 |
30 | bool started() const { return started_; } // Whether it has started
31 | const std::string name() const { return name_; } // Get name
32 |
33 | private:
34 | EventLoop *baseLoop_; // The loop created by the user using muduo. If the number of threads is 1, use the user-created loop directly; otherwise, create multiple EventLoops
35 | std::string name_; // Thread pool name, usually specified by the user. The name of EventLoopThread in the thread pool depends on the thread pool name.
36 | bool started_; // Whether it has started
37 | int numThreads_; // Number of threads in the thread pool
38 | int next_; // The index of the EventLoop selected when a new connection arrives
39 | std::vector> threads_; // List of IO threads
40 | std::vector loops_; // List of EventLoops in the thread pool, pointing to EventLoop objects created by the EventLoopThread thread function.
41 | ConsistentHash hash_; // Consistent hash object
42 | };
--------------------------------------------------------------------------------
/include/FileUtil.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include//off_t
5 | /**
6 | * @brief File utility class for handling file write operations
7 | * This class encapsulates basic file operations, including writing data and flushing the buffer
8 | */
9 | class FileUtil
10 | {
11 | public:
12 | /**
13 | * @brief Constructor
14 | * @param file_name Name of the file to open
15 | */
16 | FileUtil(std::string& file_name);
17 |
18 | /**
19 | * @brief Destructor
20 | * Responsible for closing the file and cleaning up resources
21 | */
22 | ~FileUtil();
23 |
24 | /**
25 | * @brief Write data to file
26 | * @param data Pointer to the data to write
27 | * @param len Length of the data to write
28 | */
29 | void append(const char* data, size_t len);
30 |
31 | /**
32 | * @brief Flush file buffer
33 | * Immediately write the data in the buffer to the file
34 | */
35 | void flush();
36 |
37 | /**
38 | * @brief Get the number of bytes written
39 | * @return Returns the total number of bytes written to the file
40 | */
41 | off_t writtenBytes() const { return writtenBytes_; }
42 |
43 | private:
44 | size_t write(const char* data, size_t len);
45 | FILE* file_; // File pointer for file operations
46 | char buffer_[64*1024]; // Buffer for file operations, 64KB in size, used to improve write efficiency
47 | off_t writtenBytes_; // Records the total number of bytes written to the file, off_t type for large file support
48 | };
--------------------------------------------------------------------------------
/include/FixedBuffer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | // Forward declaration of class
5 | class AsyncLogging;
6 | constexpr int kSmallBufferSize = 4000;
7 | constexpr int kLargeBufferSize = 4000 * 1000;
8 |
9 | // Fixed buffer class for managing log data storage
10 | // This class provides a fixed-size buffer that allows data to be appended to the buffer and provides related operations
11 | template
12 | class FixedBuffer : noncopyable
13 | {
14 | public:
15 | // Constructor, initialize current pointer to buffer start position
16 | FixedBuffer()
17 | : cur_(data_), size_(0)
18 | {
19 | }
20 |
21 | // Append data of specified length to buffer
22 | // If there is enough available space in the buffer, the data is copied to the current pointer position and the current pointer is updated
23 | void append(const char *buf, size_t len)
24 | {
25 | if (avail() > len)
26 | {
27 | memcpy(cur_, buf, len); // Copy data to buffer
28 | add(len);
29 | }
30 | }
31 |
32 | // Return the starting address of the buffer
33 | const char *data() const { return data_; }
34 |
35 | // Return the length of the current valid data in the buffer
36 | int length() const { return size_; }
37 |
38 | // Return the position of the current pointer
39 | char *current() { return cur_; }
40 |
41 | // Return the size of the remaining available space in the buffer
42 | size_t avail() const { return static_cast(buffer_size - size_); }
43 |
44 | // Update the current pointer, increase the specified length
45 | void add(size_t len)
46 | {
47 | cur_ += len;
48 | size_ += len;
49 | }
50 | // Reset the current pointer to the start of the buffer
51 | void reset()
52 | {
53 | cur_ = data_;
54 | size_ = 0;
55 | }
56 |
57 | // Clear the data in the buffer
58 | void bzero() { ::bzero(data_, sizeof(data_)); }
59 |
60 | // Convert the data in the buffer to std::string type and return
61 | std::string toString() const { return std::string(data_, length()); }
62 |
63 | private:
64 | char data_[buffer_size]; // Define fixed-size buffer
65 | char *cur_;
66 | int size_;
67 | };
68 |
--------------------------------------------------------------------------------
/include/InetAddress.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | // Encapsulate socket address type
8 | class InetAddress
9 | {
10 | public:
11 | explicit InetAddress(uint16_t port = 0, std::string ip = "127.0.0.1");
12 | explicit InetAddress(const sockaddr_in &addr)
13 | : addr_(addr)
14 | {
15 | }
16 |
17 | std::string toIp() const;
18 | std::string toIpPort() const;
19 | uint16_t toPort() const;
20 |
21 | const sockaddr_in *getSockAddr() const { return &addr_; }
22 | void setSockAddr(const sockaddr_in &addr) { addr_ = addr; }
23 |
24 | private:
25 | sockaddr_in addr_;
26 | };
--------------------------------------------------------------------------------
/include/LFU.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "RICachePolicy.h"
11 |
12 | namespace RonaldCache
13 | {
14 |
15 | template class RLfuCache;
16 |
17 | template
18 | class FreqList
19 | {
20 | private:
21 | struct Node
22 | {
23 | int freq; // Access frequency
24 | Key key;
25 | Value value;
26 | std::shared_ptr pre; // Previous node
27 | std::shared_ptr next;
28 |
29 | Node()
30 | : freq(1), pre(nullptr), next(nullptr) {}
31 | Node(Key key, Value value)
32 | : freq(1), key(key), value(value), pre(nullptr), next(nullptr) {}
33 | };
34 |
35 | using NodePtr = std::shared_ptr;
36 | int freq_; // Access frequency
37 | NodePtr head_; // Dummy head node
38 | NodePtr tail_; // Dummy tail node
39 |
40 | public:
41 | explicit FreqList(int n)
42 | : freq_(n)
43 | {
44 | head_ = std::make_shared();
45 | tail_ = std::make_shared();
46 | head_->next = tail_;
47 | tail_->pre = head_;
48 | }
49 |
50 | bool isEmpty() const
51 | {
52 | return head_->next == tail_;
53 | }
54 |
55 | // Node management method
56 | void addNode(NodePtr node)
57 | {
58 | if (!node || !head_ || !tail_)
59 | return;
60 |
61 | node->pre = tail_->pre;
62 | node->next = tail_;
63 | tail_->pre->next = node;
64 | tail_->pre = node;
65 | }
66 |
67 | void removeNode(NodePtr node)
68 | {
69 | if (!node || !head_ || !tail_)
70 | return;
71 | if (!node->pre || !node->next)
72 | return;
73 |
74 | node->pre->next = node->next;
75 | node->next->pre = node->pre;
76 | node->pre = nullptr;
77 | node->next = nullptr;
78 | }
79 |
80 | NodePtr getFirstNode() const { return head_->next; }
81 |
82 | friend class RLfuCache;
83 | //friend class RArcCache;
84 | };
85 |
86 | template
87 | class RLfuCache : public RICachePolicy
88 | {
89 | public:
90 | using Node = typename FreqList::Node;
91 | using NodePtr = std::shared_ptr;
92 | using NodeMap = std::unordered_map;
93 |
94 | RLfuCache(int capacity, int maxAverageNum = 10)
95 | : capacity_(capacity), minFreq_(INT8_MAX), maxAverageNum_(maxAverageNum),
96 | curAverageNum_(0), curTotalNum_(0)
97 | {}
98 |
99 | ~RLfuCache() override = default;
100 |
101 | void put(Key key, Value value) override
102 | {
103 | if (capacity_ == 0)
104 | return;
105 |
106 | std::lock_guard lock(mutex_);
107 | auto it = nodeMap_.find(key);
108 | if (it != nodeMap_.end())
109 | {
110 | // Reset its value
111 | it->second->value = value;
112 | // If found, just adjust it directly, no need to search again in get, but the impact is not significant
113 | getInternal(it->second, value);
114 | return;
115 | }
116 |
117 | putInternal(key, value);
118 | }
119 |
120 | // value is an output parameter
121 | bool get(Key key, Value& value) override
122 | {
123 | std::lock_guard lock(mutex_);
124 | auto it = nodeMap_.find(key);
125 | if (it != nodeMap_.end())
126 | {
127 | getInternal(it->second, value);
128 | return true;
129 | }
130 |
131 | return false;
132 | }
133 |
134 | Value get(Key key) override
135 | {
136 | Value value;
137 | get(key, value);
138 | return value;
139 | }
140 |
141 | // Clear cache and reclaim resources
142 | void purge()
143 | {
144 | nodeMap_.clear();
145 | freqToFreqList_.clear();
146 | }
147 |
148 | private:
149 | void putInternal(Key key, Value value); // Add to cache
150 | void getInternal(NodePtr node, Value& value); // Get from cache
151 |
152 | void kickOut(); // Remove expired data from cache
153 |
154 | void removeFromFreqList(NodePtr node); // Remove node from frequency list
155 | void addToFreqList(NodePtr node); // Add to frequency list
156 |
157 | void addFreqNum(); // Increase average access frequency
158 | void decreaseFreqNum(int num); // Decrease average access frequency
159 | void handleOverMaxAverageNum(); // Handle case when current average access frequency exceeds limit
160 | void updateMinFreq();
161 |
162 | private:
163 | int capacity_; // Cache capacity
164 | int minFreq_; // Minimum access frequency (used to find node with minimum access frequency)
165 | int maxAverageNum_; // Maximum average access frequency
166 | int curAverageNum_; // Current average access frequency
167 | int curTotalNum_; // Total number of accesses to all cache
168 | std::mutex mutex_; // Mutex for synchronization
169 | NodeMap nodeMap_; // Mapping from key to cache node
170 | std::unordered_map*> freqToFreqList_; // Mapping from access frequency to frequency list
171 | };
172 |
173 | template
174 | void RLfuCache::getInternal(NodePtr node, Value& value)
175 | {
176 | // After finding it, remove it from the low-frequency list and add it to the list with frequency +1,
177 | // Access frequency +1, then return the value
178 | value = node->value;
179 | // Remove node from the original frequency list
180 | removeFromFreqList(node);
181 | node->freq++;
182 | addToFreqList(node);
183 | // If the current node's access frequency equals minFreq+1 and its predecessor list is empty, then
184 | // the freqToFreqList_[node->freq - 1] list is empty due to node migration, so the minimum access frequency needs to be updated
185 | if (node->freq - 1 == minFreq_ && freqToFreqList_[node->freq - 1]->isEmpty())
186 | minFreq_++;
187 |
188 | // The total access frequency and the current average access frequency both increase accordingly
189 | addFreqNum();
190 | }
191 |
192 | template
193 | void RLfuCache::putInternal(Key key, Value value)
194 | {
195 | // If not in the cache, need to check whether the cache is full
196 | if (nodeMap_.size() == capacity_)
197 | {
198 | // If the cache is full, delete the least frequently used node and update the current average and total access frequency
199 | kickOut();
200 | }
201 |
202 | // Create a new node, add it, and update the minimum access frequency
203 | NodePtr node = std::make_shared(key, value);
204 | nodeMap_[key] = node;
205 | addToFreqList(node);
206 | addFreqNum();
207 | minFreq_ = std::min(minFreq_, 1);
208 | }
209 |
210 | template
211 | void RLfuCache::kickOut()
212 | {
213 | NodePtr node = freqToFreqList_[minFreq_]->getFirstNode();
214 | removeFromFreqList(node);
215 | nodeMap_.erase(node->key);
216 | decreaseFreqNum(node->freq);
217 | }
218 |
219 | template
220 | void RLfuCache::removeFromFreqList(NodePtr node)
221 | {
222 | // Check if node is null
223 | if (!node)
224 | return;
225 |
226 | auto freq = node->freq;
227 | freqToFreqList_[freq]->removeNode(node);
228 | }
229 |
230 | template
231 | void RLfuCache::addToFreqList(NodePtr node)
232 | {
233 | // Check if node is null
234 | if (!node)
235 | return;
236 |
237 | // Add to frequency list
238 | auto freq = node->freq;
239 | if (freqToFreqList_.find(node->freq) == freqToFreqList_.end())
240 | {
241 | // If it doesn't exist, create it
242 | freqToFreqList_[node->freq] = new FreqList(node->freq);
243 | }
244 |
245 | freqToFreqList_[freq]->addNode(node);
246 | }
247 |
248 | template
249 | void RLfuCache::addFreqNum()
250 | {
251 | curTotalNum_++;
252 | if (nodeMap_.empty())
253 | curAverageNum_ = 0;
254 | else
255 | curAverageNum_ = curTotalNum_ / nodeMap_.size();
256 |
257 | if (curAverageNum_ > maxAverageNum_)
258 | {
259 | handleOverMaxAverageNum();
260 | }
261 | }
262 |
263 | template
264 | void RLfuCache::decreaseFreqNum(int num)
265 | {
266 | // Decrease average access frequency
267 | curTotalNum_ -= num;
268 | if (nodeMap_.empty())
269 | curAverageNum_ = 0;
270 | else
271 | curAverageNum_ = curTotalNum_ / nodeMap_.size();
272 | }
273 |
274 | template
275 | void RLfuCache::handleOverMaxAverageNum()
276 | {
277 | if (nodeMap_.empty())
278 | return;
279 |
280 | // Current average access frequency already exceeds limit, all node access frequencies - (maxAverageNum_ / 2)
281 | for (auto it = nodeMap_.begin(); it != nodeMap_.end(); ++it)
282 | {
283 | // Check if node is null
284 | if (!it->second)
285 | continue;
286 |
287 | NodePtr node = it->second;
288 |
289 | // Remove from current frequency list
290 | removeFromFreqList(node);
291 |
292 | // Decrease frequency
293 | node->freq -= maxAverageNum_ / 2;
294 | if (node->freq < 1) node->freq = 1;
295 |
296 | // Add to new frequency list
297 | addToFreqList(node);
298 | }
299 |
300 | // Update minimum frequency
301 | updateMinFreq();
302 | }
303 |
304 | template
305 | void RLfuCache::updateMinFreq()
306 | {
307 | minFreq_ = INT8_MAX;
308 | for (const auto& pair : freqToFreqList_)
309 | {
310 | if (pair.second && !pair.second->isEmpty())
311 | {
312 | minFreq_ = std::min(minFreq_, pair.first);
313 | }
314 | }
315 | if (minFreq_ == INT8_MAX)
316 | minFreq_ = 1;
317 | }
318 |
319 | // The total access frequency and the current average access frequency both increase accordingly
320 | // It does not sacrifice space for time, but shards the original cache size.
321 | template
322 | class RHashLfuCache
323 | {
324 | public:
325 | RHashLfuCache(size_t capacity, int sliceNum, int maxAverageNum = 10)
326 | : sliceNum_(sliceNum > 0 ? sliceNum : std::thread::hardware_concurrency())
327 | , capacity_(capacity)
328 | {
329 | size_t sliceSize = std::ceil(capacity_ / static_cast(sliceNum_)); // Capacity of each lfu shard
330 | for (int i = 0; i < sliceNum_; ++i)
331 | {
332 | lfuSliceCaches_.emplace_back(new RLfuCache(sliceSize, maxAverageNum));
333 | }
334 | }
335 |
336 | void put(Key key, Value value)
337 | {
338 | // Find the corresponding lfu shard according to the key
339 | size_t sliceIndex = Hash(key) % sliceNum_;
340 | return lfuSliceCaches_[sliceIndex]->put(key, value);
341 | }
342 |
343 | bool get(Key key, Value& value)
344 | {
345 | // Find the corresponding lfu shard according to the key
346 | size_t sliceIndex = Hash(key) % sliceNum_;
347 | return lfuSliceCaches_[sliceIndex]->get(key, value);
348 | }
349 |
350 | Value get(Key key)
351 | {
352 | Value value;
353 | get(key, value);
354 | return value;
355 | }
356 |
357 | // Clear cache
358 | void purge()
359 | {
360 | for (auto& lfuSliceCache : lfuSliceCaches_)
361 | {
362 | lfuSliceCache->purge();
363 | }
364 | }
365 |
366 | private:
367 | // Calculate the corresponding hash value for the key
368 | size_t Hash(Key key)
369 | {
370 | std::hash hashFunc;
371 | return hashFunc(key);
372 | }
373 |
374 | private:
375 | size_t capacity_; // Total cache capacity
376 | int sliceNum_; // Number of cache shards
377 | std::vector>> lfuSliceCaches_; // Container for lfu cache shards
378 | };
379 |
380 | } // namespace RonaldCache
381 |
--------------------------------------------------------------------------------
/include/LogFile.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include "FileUtil.h"
3 | #include
4 | #include
5 | #include
6 | /**
7 | * @brief Log file management class
8 | * Responsible for log file creation, writing, rolling and flushing operations
9 | * Supports automatic log file rolling by size and time
10 | */
11 | class LogFile
12 | {
13 | public:
14 | /**
15 | * @brief Constructor
16 | * @param basename Basic name of log file
17 | * @param rollsize When log file size reaches this many bytes, roll the file, unit: bytes
18 | * @param flushInterval Log flush interval time, default 3 seconds
19 | * @param checkEveryN_ Check if rolling is needed after writing checkEveryN_ times, default 1024 times
20 | */
21 | LogFile(const std::string &basename,
22 | off_t rollsize,
23 | int flushInterval = 3,
24 | int checkEveryN_ = 1024);
25 | ~LogFile();
26 | /**
27 | * @brief Append data to log file
28 | * @param data Data to be written
29 | * @param len Data length
30 | */
31 | void append(const char *data,int len);
32 |
33 | /**
34 | * @brief Force flush buffer data to disk
35 | */
36 | void flush();
37 |
38 | /**
39 | * @brief Roll log file
40 | * Create new log file when log file size exceeds rollsize_ or time exceeds one day
41 | * @return Whether roll log file successfully
42 | */
43 | bool rollFile();
44 |
45 | private:
46 | /**
47 | * @brief Disable destructor, use smart pointer management
48 | */
49 |
50 |
51 | /**
52 | * @brief Generate log file name
53 | * @param basename Basic name of log file
54 | * @param now Current time pointer
55 | * @return Complete log file name, format: basename.YYYYmmdd-HHMMSS.log
56 | */
57 | static std::string getLogFileName(const std::string &basename, time_t *now);
58 |
59 | /**
60 | * @brief Append data in locked state
61 | * @param data Data to be written
62 | * @param len Data length
63 | */
64 | void appendInlock(const char *data, int len);
65 |
66 | const std::string basename_;
67 | const off_t rollsize_; // Roll file size
68 | const int flushInterval_; // Flush time limit, default 3s
69 | const int checkEveryN_; // Write data count limit, default 1024
70 |
71 | int count_; // Write data count, cleared when exceeding limit checkEveryN_, then recount
72 |
73 | std::mutex mutex_;
74 | time_t startOfPeriod_;// Start time of current log writing period (seconds)
75 | time_t lastRoll_;// Last log file roll time (seconds)
76 | time_t lastFlush_; // Last log file flush time (seconds)
77 | std::unique_ptr file_;
78 | const static int kRollPerSeconds_ = 60*60*24;
79 | };
80 |
--------------------------------------------------------------------------------
/include/LogStream.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 | #include
3 | #include
4 | #include "noncopyable.h"
5 | #include "FixedBuffer.h"
6 | class GeneralTemplate : noncopyable
7 | {
8 | public:
9 | GeneralTemplate()
10 | : data_(nullptr),
11 | len_(0)
12 | {}
13 |
14 | explicit GeneralTemplate(const char* data, int len)
15 | : data_(data),
16 | len_(len)
17 | {}
18 |
19 | const char* data_;
20 | int len_;
21 | };
22 | // LogStream class is used to manage log output stream, overloads the output stream operator << to write various types of values into internal buffer
23 | class LogStream : noncopyable
24 | {
25 | public:
26 | // Define a Buffer type, using fixed-size buffer
27 | using Buffer = FixedBuffer;
28 |
29 | // Append specified length of character data to buffer
30 | void append(const char *buffer, int len)
31 | {
32 | buffer_.append(buffer, len); // Call Buffer's append method
33 | }
34 |
35 | // Return constant reference to current buffer
36 | const Buffer &buffer() const
37 | {
38 | return buffer_; // Return current buffer
39 | }
40 |
41 | // Reset buffer, reset current pointer to buffer start
42 | void reset_buffer()
43 | {
44 | buffer_.reset(); // Call Buffer's reset method
45 | }
46 |
47 | // Overload output stream operator <<, used to write boolean value to buffer
48 | LogStream &operator<<(bool express);
49 |
50 | // Overload output stream operator <<, used to write short integer to buffer
51 | LogStream &operator<<(short number);
52 | // Overload output stream operator <<, used to write unsigned short integer to buffer
53 | LogStream &operator<<(unsigned short);
54 | // Overload output stream operator <<, used to write integer to buffer
55 | LogStream &operator<<(int);
56 | // Overload output stream operator <<, used to write unsigned integer to buffer
57 | LogStream &operator<<(unsigned int);
58 | // Overload output stream operator <<, used to write long integer to buffer
59 | LogStream &operator<<(long);
60 | // Overload output stream operator <<, used to write unsigned long integer to buffer
61 | LogStream &operator<<(unsigned long);
62 | // Overload output stream operator <<, used to write long long integer to buffer
63 | LogStream &operator<<(long long);
64 | // Overload output stream operator <<, used to write unsigned long long integer to buffer
65 | LogStream &operator<<(unsigned long long);
66 |
67 | // Overload output stream operator <<, used to write float number to buffer
68 | LogStream &operator<<(float number);
69 | // Overload output stream operator <<, used to write double precision float number to buffer
70 | LogStream &operator<<(double);
71 |
72 | // Overload output stream operator <<, used to write character to buffer
73 | LogStream &operator<<(char str);
74 | // Overload output stream operator <<, used to write C-style string to buffer
75 | LogStream &operator<<(const char *);
76 | // Overload output stream operator <<, used to write unsigned character pointer to buffer
77 | LogStream &operator<<(const unsigned char *);
78 | // Overload output stream operator <<, used to write std::string object to buffer
79 | LogStream &operator<<(const std::string &);
80 | // (const char*, int) overload
81 | LogStream& operator<<(const GeneralTemplate& g);
82 | private:
83 | // Define maximum number size constant
84 | static constexpr int kMaxNumberSize = 32;
85 |
86 | // For integer types, special processing is needed, template function used to format integer
87 | template
88 | void formatInteger(T num);
89 |
90 | // Internal buffer object
91 | Buffer buffer_;
92 | };
93 |
--------------------------------------------------------------------------------
/include/Logger.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include "LogStream.h"
9 | #include
10 | #include "Timestamp.h"
11 |
12 | #define OPEN_LOGGING
13 |
14 | // SourceFile's purpose is to extract the file name
15 | class SourceFile
16 | {
17 | public:
18 | explicit SourceFile(const char* filename)
19 | : data_(filename)
20 | {
21 | /**
22 | * Find the last occurrence of / in data to get the specific file name
23 | * 2022/10/26/test.log
24 | */
25 | const char* slash = strrchr(filename, '/');
26 | if (slash)
27 | {
28 | data_ = slash + 1;
29 | }
30 | size_ = static_cast(strlen(data_));
31 | }
32 |
33 | const char* data_;
34 | int size_;
35 | };
36 | class Logger
37 | {
38 | public:
39 | enum LogLevel
40 | {
41 | TRACE,
42 | DEBUG,
43 | INFO,
44 | WARN,
45 | ERROR,
46 | FATAL,
47 | LEVEL_COUNT,
48 | };
49 | Logger(const char *filename, int line, LogLevel level);
50 | ~Logger();
51 | // Stream can be changed
52 | LogStream& stream() { return impl_.stream_; }
53 |
54 |
55 | // Output function and flush buffer function
56 | using OutputFunc = std::function;
57 | using FlushFunc = std::function;
58 | static void setOutput(OutputFunc);
59 | static void setFlush(FlushFunc);
60 |
61 | private:
62 | class Impl
63 | {
64 | public:
65 | using LogLevel=Logger::LogLevel;
66 | Impl(LogLevel level,int savedErrno,const char *filename, int line);
67 | void formatTime();
68 | void finish(); // Add a suffix to a log message
69 |
70 | Timestamp time_;
71 | LogStream stream_;
72 | LogLevel level_;
73 | int line_;
74 | SourceFile basename_;
75 | };
76 |
77 | private:
78 | Impl impl_;
79 | };
80 |
81 | // Get errno information
82 | const char* getErrnoMsg(int savedErrno);
83 | /**
84 | * Only output when log level is less than the corresponding level
85 | * For example, if level is set to FATAL, then logLevel is greater than DEBUG and INFO, so DEBUG and INFO level logs won't be output
86 | */
87 | #ifdef OPEN_LOGGING
88 | #define LOG_DEBUG Logger(__FILE__, __LINE__, Logger::DEBUG).stream()
89 | #define LOG_INFO Logger(__FILE__, __LINE__, Logger::INFO).stream()
90 | #define LOG_WARN Logger(__FILE__, __LINE__, Logger::WARN).stream()
91 | #define LOG_ERROR Logger(__FILE__, __LINE__, Logger::ERROR).stream()
92 | #define LOG_FATAL Logger(__FILE__, __LINE__, Logger::FATAL).stream()
93 | #else
94 | #define LOG(level) LogStream()
95 | #endif
--------------------------------------------------------------------------------
/include/Poller.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 |
6 | #include "noncopyable.h"
7 | #include "Timestamp.h"
8 |
9 | class Channel;
10 | class EventLoop;
11 |
12 | // Core IO multiplexing module for event dispatcher in muduo library
13 | class Poller
14 | {
15 | public:
16 | using ChannelList = std::vector;
17 |
18 | Poller(EventLoop *loop);
19 | virtual ~Poller() = default;
20 |
21 | // Preserve unified interface for all IO multiplexing
22 | virtual Timestamp poll(int timeoutMs, ChannelList *activeChannels) = 0;
23 | virtual void updateChannel(Channel *channel) = 0;
24 | virtual void removeChannel(Channel *channel) = 0;
25 |
26 | // Check if the parameter channel is in the current Poller
27 | bool hasChannel(Channel *channel) const;
28 |
29 | // EventLoop can get the specific implementation of default IO multiplexing through this interface
30 | static Poller *newDefaultPoller(EventLoop *loop);
31 |
32 | protected:
33 | // map's key: sockfd value: channel type that sockfd belongs to
34 | using ChannelMap = std::unordered_map;
35 | ChannelMap channels_;
36 |
37 | private:
38 | EventLoop *ownerLoop_; // Define the EventLoop that Poller belongs to
39 | };
--------------------------------------------------------------------------------
/include/RICachePolicy.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | namespace RonaldCache
4 | {
5 |
6 | template
7 | class RICachePolicy
8 | {
9 | public:
10 | virtual ~RICachePolicy() {};
11 |
12 | // Add cache interface
13 | virtual void put(Key key, Value value) = 0;
14 |
15 | // key is the input parameter, accessed value is returned as an output parameter | returns true if access is successful
16 | virtual bool get(Key key, Value& value) = 0;
17 | // If key is found in cache, return value directly
18 | virtual Value get(Key key) = 0;
19 |
20 | };
21 |
22 | } // namespace RonaldCache
--------------------------------------------------------------------------------
/include/Socket.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include "noncopyable.h"
4 |
5 | class InetAddress;
6 |
7 | // Encapsulate socket fd
8 | class Socket : noncopyable
9 | {
10 | public:
11 | explicit Socket(int sockfd)
12 | : sockfd_(sockfd)
13 | {
14 | }
15 | ~Socket();
16 |
17 | int fd() const { return sockfd_; }
18 | void bindAddress(const InetAddress &localaddr);
19 | void listen();
20 | int accept(InetAddress *peeraddr);
21 |
22 | void shutdownWrite();
23 |
24 | void setTcpNoDelay(bool on);
25 | void setReuseAddr(bool on);
26 | void setReusePort(bool on);
27 | void setKeepAlive(bool on);
28 |
29 | private:
30 | const int sockfd_;
31 | };
32 |
--------------------------------------------------------------------------------
/include/TcpConnection.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | #include "noncopyable.h"
8 | #include "InetAddress.h"
9 | #include "Callbacks.h"
10 | #include "Buffer.h"
11 | #include "Timestamp.h"
12 |
13 | class Channel;
14 | class EventLoop;
15 | class Socket;
16 |
17 | /**
18 | * TcpServer => Acceptor => A new user connection is obtained through the accept function to get connfd
19 | * => TcpConnection sets callback => sets to Channel => Poller => Channel callback
20 | **/
21 |
22 | class TcpConnection : noncopyable, public std::enable_shared_from_this
23 | {
24 | public:
25 | TcpConnection(EventLoop *loop,
26 | const std::string &nameArg,
27 | int sockfd,
28 | const InetAddress &localAddr,
29 | const InetAddress &peerAddr);
30 | ~TcpConnection();
31 |
32 | EventLoop *getLoop() const { return loop_; }
33 | const std::string &name() const { return name_; }
34 | const InetAddress &localAddress() const { return localAddr_; }
35 | const InetAddress &peerAddress() const { return peerAddr_; }
36 |
37 | bool connected() const { return state_ == kConnected; }
38 |
39 | // Send data
40 | void send(const std::string &buf);
41 | void sendFile(int fileDescriptor, off_t offset, size_t count);
42 |
43 | // Close half connection
44 | void shutdown();
45 |
46 | void setConnectionCallback(const ConnectionCallback &cb)
47 | { connectionCallback_ = cb; }
48 | void setMessageCallback(const MessageCallback &cb)
49 | { messageCallback_ = cb; }
50 | void setWriteCompleteCallback(const WriteCompleteCallback &cb)
51 | { writeCompleteCallback_ = cb; }
52 | void setCloseCallback(const CloseCallback &cb)
53 | { closeCallback_ = cb; }
54 | void setHighWaterMarkCallback(const HighWaterMarkCallback &cb, size_t highWaterMark)
55 | { highWaterMarkCallback_ = cb; highWaterMark_ = highWaterMark; }
56 |
57 | // Connection established
58 | void connectEstablished();
59 | // Connection destroyed
60 | void connectDestroyed();
61 |
62 | private:
63 | enum StateE
64 | {
65 | kDisconnected, // Already disconnected
66 | kConnecting, // Connecting
67 | kConnected, // Connected
68 | kDisconnecting // Disconnecting
69 | };
70 | void setState(StateE state) { state_ = state; }
71 |
72 | void handleRead(Timestamp receiveTime);
73 | void handleWrite();//Handle write event
74 | void handleClose();
75 | void handleError();
76 |
77 | void sendInLoop(const void *data, size_t len);
78 | void shutdownInLoop();
79 | void sendFileInLoop(int fileDescriptor, off_t offset, size_t count);
80 | EventLoop *loop_; // Here is baseloop or subloop determined by the number of threads created in TcpServer. If it is multi-Reactor, this loop_ points to subloop. If it is single-Reactor, this loop_ points to baseloop
81 | const std::string name_;
82 | std::atomic_int state_;
83 | bool reading_;//Whether the connection is listening for read events
84 |
85 | // Socket Channel here is similar to Acceptor. Acceptor => mainloop TcpConnection => subloop
86 | std::unique_ptr socket_;
87 | std::unique_ptr channel_;
88 |
89 | const InetAddress localAddr_;
90 | const InetAddress peerAddr_;
91 |
92 | // These callbacks are also in TcpServer. Users register by writing to TcpServer. TcpServer then passes the registered callbacks to TcpConnection, and TcpConnection registers the callbacks to Channel
93 | ConnectionCallback connectionCallback_; // Callback when there is a new connection
94 | MessageCallback messageCallback_; // Callback when there is a read/write message
95 | WriteCompleteCallback writeCompleteCallback_; // Callback after the message is sent
96 | HighWaterMarkCallback highWaterMarkCallback_; // High water mark callback
97 | CloseCallback closeCallback_; // Callback for closing connection
98 | size_t highWaterMark_; // High water mark threshold
99 |
100 | // Data buffer
101 | Buffer inputBuffer_; // Buffer for receiving data
102 | Buffer outputBuffer_; // Buffer for sending data. User sends to outputBuffer_
103 | };
104 |
--------------------------------------------------------------------------------
/include/TcpServer.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | /**
4 | * User uses muduo to write server programs
5 | **/
6 |
7 | #include
8 | #include
9 | #include
10 | #include
11 | #include
12 |
13 | #include "EventLoop.h"
14 | #include "Acceptor.h"
15 | #include "InetAddress.h"
16 | #include "noncopyable.h"
17 | #include "EventLoopThreadPool.h"
18 | #include "Callbacks.h"
19 | #include "TcpConnection.h"
20 | #include "Buffer.h"
21 |
22 | // Class used for server programming
23 | class TcpServer
24 | {
25 | public:
26 | using ThreadInitCallback = std::function;
27 |
28 | enum Option
29 | {
30 | kNoReusePort,// Do not allow reuse of local port
31 | kReusePort,// Allow reuse of local port
32 | };
33 |
34 | TcpServer(EventLoop *loop,
35 | const InetAddress &listenAddr,
36 | const std::string &nameArg,
37 | Option option = kNoReusePort);
38 | ~TcpServer();
39 |
40 | void setThreadInitCallback(const ThreadInitCallback &cb) { threadInitCallback_ = cb; }
41 | void setConnectionCallback(const ConnectionCallback &cb) { connectionCallback_ = cb; }
42 | void setMessageCallback(const MessageCallback &cb) { messageCallback_ = cb; }
43 | void setWriteCompleteCallback(const WriteCompleteCallback &cb) { writeCompleteCallback_ = cb; }
44 |
45 | // Set the number of underlying subloops
46 | void setThreadNum(int numThreads);
47 | /**
48 | * If not listening, start the server (listen).
49 | * Multiple calls have no side effects.
50 | * Thread safe.
51 | */
52 | void start();
53 |
54 | private:
55 | void newConnection(int sockfd, const InetAddress &peerAddr);
56 | void removeConnection(const TcpConnectionPtr &conn);
57 | void removeConnectionInLoop(const TcpConnectionPtr &conn);
58 |
59 | using ConnectionMap = std::unordered_map;
60 |
61 | EventLoop *loop_; // baseloop user-defined loop
62 |
63 | const std::string ipPort_;
64 | const std::string name_;
65 |
66 | std::unique_ptr acceptor_; // Runs in mainloop, task is to listen for new connection events
67 |
68 | std::shared_ptr threadPool_; // one loop per thread
69 |
70 | ConnectionCallback connectionCallback_; // Callback when there is a new connection
71 | MessageCallback messageCallback_; // Callback when read/write events occur
72 | WriteCompleteCallback writeCompleteCallback_; // Callback after message is sent
73 |
74 | ThreadInitCallback threadInitCallback_; // Callback for loop thread initialization
75 | int numThreads_;// Number of threads in the thread pool
76 | std::atomic_int started_;
77 | int nextConnId_;
78 | ConnectionMap connections_; // Store all connections
79 | };
--------------------------------------------------------------------------------
/include/Thread.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | #include "noncopyable.h"
11 |
12 | class Thread : noncopyable
13 | {
14 | public:
15 | using ThreadFunc = std::function;
16 |
17 | explicit Thread(ThreadFunc, const std::string &name = std::string());
18 | ~Thread();
19 |
20 | void start();
21 | void join();
22 |
23 | bool started() { return started_; }
24 | pid_t tid() const { return tid_; }
25 | const std::string &name() const { return name_; }
26 |
27 | static int numCreated() { return numCreated_; }
28 |
29 | private:
30 | void setDefaultName();
31 |
32 | bool started_;
33 | bool joined_;
34 | std::shared_ptr thread_;
35 | pid_t tid_; // Will be bound when thread is created
36 | ThreadFunc func_; // Thread callback function
37 | std::string name_;
38 | static std::atomic_int numCreated_;
39 | };
--------------------------------------------------------------------------------
/include/Timer.h:
--------------------------------------------------------------------------------
1 | #ifndef TIMER_H
2 | #define TIMER_H
3 |
4 | #include "noncopyable.h"
5 | #include "Timestamp.h"
6 | #include
7 |
8 | /**
9 | * Timer is used to describe a timer
10 | * Timer callback function, next timeout moment, time interval for repeating timers, etc.
11 | */
12 | class Timer : noncopyable
13 | {
14 | public:
15 | using TimerCallback = std::function;
16 |
17 | Timer(TimerCallback cb, Timestamp when, double interval)
18 | : callback_(move(cb)),
19 | expiration_(when),
20 | interval_(interval),
21 | repeat_(interval > 0.0) // Set to 0 for one-time timer
22 | {
23 | }
24 |
25 | void run() const
26 | {
27 | callback_();
28 | }
29 |
30 | Timestamp expiration() const { return expiration_; }
31 | bool repeat() const { return repeat_; }
32 |
33 | // Restart timer (if it's a non-repeating event, set expiration time to 0)
34 | void restart(Timestamp now);
35 |
36 | private:
37 | const TimerCallback callback_; // Timer callback function
38 | Timestamp expiration_; // Next timeout moment
39 | const double interval_; // Timeout interval, if it's a one-time timer, this value is 0
40 | const bool repeat_; // Whether to repeat (false means it's a one-time timer)
41 | };
42 |
43 | #endif // TIMER_H
--------------------------------------------------------------------------------
/include/TimerQueue.h:
--------------------------------------------------------------------------------
1 | #ifndef TIMER_QUEUE_H
2 | #define TIMER_QUEUE_H
3 |
4 | #include "Timestamp.h"
5 | #include "Channel.h"
6 |
7 | #include
8 | #include
9 |
10 | class EventLoop;
11 | class Timer;
12 |
13 | class TimerQueue
14 | {
15 | public:
16 | using TimerCallback = std::function;
17 |
18 | explicit TimerQueue(EventLoop* loop);
19 | ~TimerQueue();
20 |
21 | // Insert timer (callback function, expiration time, whether to repeat)
22 | void addTimer(TimerCallback cb,
23 | Timestamp when,
24 | double interval);
25 |
26 | private:
27 | using Entry = std::pair; // Use timestamp as key to get timer
28 | using TimerList = std::set; // Underlying implementation uses red-black tree, automatically sorted by timestamp
29 |
30 | // Add timer in this loop
31 | // Thread safe
32 | void addTimerInLoop(Timer* timer);
33 |
34 | // Function triggered by timer read event
35 | void handleRead();
36 |
37 | // Reset timerfd_
38 | void resetTimerfd(int timerfd_, Timestamp expiration);
39 |
40 | // Remove all expired timers
41 | // 1. Get expired timers
42 | // 2. Reset these timers (destroy or repeat timer tasks)
43 | std::vector getExpired(Timestamp now);
44 | void reset(const std::vector& expired, Timestamp now);
45 |
46 | // Internal method to insert timer
47 | bool insert(Timer* timer);
48 |
49 | EventLoop* loop_; // The EventLoop it belongs to
50 | const int timerfd_; // timerfd is the timer interface provided by Linux
51 | Channel timerfdChannel_; // Encapsulates timerfd_ file descriptor
52 | // Timer list sorted by expiration
53 | TimerList timers_; // Timer queue (internal implementation is red-black tree)
54 |
55 | bool callingExpiredTimers_; // Indicates that expired timers are being retrieved
56 | };
57 |
58 | #endif // TIMER_QUEUE_H
--------------------------------------------------------------------------------
/include/Timestamp.h:
--------------------------------------------------------------------------------
1 | #ifndef TIME_STAMP_H
2 | #define TIME_STAMP_H
3 |
4 | #include
5 | #include
6 | #include
7 |
8 | class Timestamp
9 | {
10 | public:
11 | Timestamp()
12 | : microSecondsSinceEpoch_(0)
13 | {
14 | }
15 |
16 | explicit Timestamp(int64_t microSecondsSinceEpoch)
17 | : microSecondsSinceEpoch_(microSecondsSinceEpoch)
18 | {
19 | }
20 |
21 | // Get current timestamp
22 | static Timestamp now();
23 | std::string toString()const;
24 |
25 | // Format: "%4d year %02d month %02d day Week %d %02d:%02d:%02d.%06d", hours:minutes:seconds.microseconds
26 | std::string toFormattedString(bool showMicroseconds = false) const;
27 |
28 | // Return microseconds of current timestamp
29 | int64_t microSecondsSinceEpoch() const { return microSecondsSinceEpoch_; }
30 | // Return seconds of current timestamp
31 | time_t secondsSinceEpoch() const
32 | {
33 | return static_cast(microSecondsSinceEpoch_ / kMicroSecondsPerSecond);
34 | }
35 |
36 | // Invalid timestamp, returns a Timestamp with value 0
37 | static Timestamp invalid()
38 | {
39 | return Timestamp();
40 | }
41 |
42 | // 1 second = 1000*1000 microseconds
43 | static const int kMicroSecondsPerSecond = 1000 * 1000;
44 |
45 | private:
46 | // Represents microseconds of timestamp (microseconds elapsed since epoch)
47 | int64_t microSecondsSinceEpoch_;
48 | };
49 |
50 | /**
51 | * Timer needs to compare timestamps, so operators need to be overloaded
52 | */
53 | inline bool operator<(Timestamp lhs, Timestamp rhs)
54 | {
55 | return lhs.microSecondsSinceEpoch() < rhs.microSecondsSinceEpoch();
56 | }
57 |
58 | inline bool operator==(Timestamp lhs, Timestamp rhs)
59 | {
60 | return lhs.microSecondsSinceEpoch() == rhs.microSecondsSinceEpoch();
61 | }
62 |
63 | // If it's a repeating timer task, this timestamp will be increased
64 | inline Timestamp addTime(Timestamp timestamp, double seconds)
65 | {
66 | // Convert delay seconds to microseconds
67 | int64_t delta = static_cast(seconds * Timestamp::kMicroSecondsPerSecond);
68 | // Return timestamp after adding time
69 | return Timestamp(timestamp.microSecondsSinceEpoch() + delta);
70 | }
71 |
72 | #endif // TIME_STAMP_H
--------------------------------------------------------------------------------
/include/memoryPool.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | namespace memoryPool
10 | {
11 | #define MEMORY_POOL_NUM 64
12 | #define SLOT_BASE_SIZE 8
13 | #define MAX_SLOT_SIZE 512
14 |
15 |
16 | /* The slot size of a specific memory pool cannot be determined, because each memory pool has different slot sizes (multiples of 8)
17 | So the sizeof of this slot structure is not the actual slot size */
18 | struct Slot
19 | {
20 | Slot* next;
21 | };
22 |
23 | class MemoryPool
24 | {
25 | public:
26 | MemoryPool(size_t BlockSize = 4096);
27 | ~MemoryPool();
28 |
29 | void init(size_t);
30 |
31 | void* allocate();
32 | void deallocate(void*);
33 | private:
34 | void allocateNewBlock();
35 | size_t padPointer(char* p, size_t align);
36 |
37 | private:
38 | int BlockSize_; // Memory block size
39 | int SlotSize_; // Slot size
40 | Slot* firstBlock_; // Points to the first actual memory block managed by the memory pool
41 | Slot* curSlot_; // Points to the current unused slot
42 | Slot* freeList_; // Points to free slots (slots that have been used and then released)
43 | Slot* lastSlot_; // As a position identifier for the last element that can be stored in the current memory block
44 | std::mutex mutexForFreeList_; // Ensure atomicity of freeList_ in multi-threaded operations
45 | std::mutex mutexForBlock_; // Ensure unnecessary repeated memory allocation in multi-threaded situations
46 | };
47 |
48 |
49 | class HashBucket
50 | {
51 | public:
52 | static void initMemoryPool();
53 | static MemoryPool& getMemoryPool(int index);
54 |
55 | static void* useMemory(size_t size)
56 | {
57 | if (size <= 0)
58 | return nullptr;
59 | if (size > MAX_SLOT_SIZE) // For memory larger than 512 bytes, use new
60 | return operator new(size);
61 |
62 | // Equivalent to size / 8 rounded up (because allocated memory can only be larger, not smaller)
63 | return getMemoryPool(((size + 7) / SLOT_BASE_SIZE) - 1).allocate();
64 | }
65 |
66 | static void freeMemory(void* ptr, size_t size)
67 | {
68 | if (!ptr)
69 | return;
70 | if (size > MAX_SLOT_SIZE)
71 | {
72 | operator delete(ptr);
73 | return;
74 | }
75 |
76 | getMemoryPool(((size + 7) / SLOT_BASE_SIZE) - 1).deallocate(ptr);
77 | }
78 |
79 | template
80 | friend T* newElement(Args&&... args);
81 |
82 | template
83 | friend void deleteElement(T* p);
84 | };
85 |
86 | template
87 | T* newElement(Args&&... args)
88 | {
89 | T* p = nullptr;
90 | // Select appropriate memory pool to allocate memory based on element size
91 | if ((p = reinterpret_cast(HashBucket::useMemory(sizeof(T)))) != nullptr)
92 | // Construct object on allocated memory
93 | new(p) T(std::forward(args)...);
94 |
95 | return p;
96 | }
97 |
98 | template
99 | void deleteElement(T* p)
100 | {
101 | // Object destruction
102 | if (p)
103 | {
104 | p->~T();
105 | // Memory recycling
106 | HashBucket::freeMemory(reinterpret_cast(p), sizeof(T));
107 | }
108 | }
109 |
110 | } // namespace memoryPool
--------------------------------------------------------------------------------
/include/noncopyable.h:
--------------------------------------------------------------------------------
1 | #pragma once // Prevent header file from being included multiple times
2 |
3 | /**
4 | * After inheriting from noncopyable, derived class objects can be constructed and destructed normally, but cannot be copy constructed or assignment constructed
5 | **/
6 | class noncopyable
7 | {
8 | public:
9 | noncopyable(const noncopyable &) = delete;
10 | noncopyable &operator=(const noncopyable &) = delete;
11 | // void operator=(const noncopyable &) = delete; // muduo changes return value to void, which is actually fine
12 | protected:
13 | noncopyable() = default;
14 | ~noncopyable() = default;
15 | };
--------------------------------------------------------------------------------
/lib/liblog_lib.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronald-debugging/Ronald-webserver/5bc27cb51466a32851dc6888e7d354c60cf09206/lib/liblog_lib.so
--------------------------------------------------------------------------------
/lib/libmemory_lib.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronald-debugging/Ronald-webserver/5bc27cb51466a32851dc6888e7d354c60cf09206/lib/libmemory_lib.so
--------------------------------------------------------------------------------
/lib/libsrc_lib.so:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/ronald-debugging/Ronald-webserver/5bc27cb51466a32851dc6888e7d354c60cf09206/lib/libsrc_lib.so
--------------------------------------------------------------------------------
/log/AsyncLogging.cc:
--------------------------------------------------------------------------------
1 | #include "AsyncLogging.h"
2 | #include
3 | AsyncLogging::AsyncLogging(const std::string &basename, off_t rollSize, int flushInterval)
4 | :
5 | flushInterval_(flushInterval),
6 | running_(false),
7 | basename_(basename),
8 | rollSize_(rollSize),
9 | thread_(std::bind(&AsyncLogging::threadFunc, this), "Logging"),
10 | mutex_(),
11 | cond_(),
12 | currentBuffer_(new LargeBuffer),
13 | nextBuffer_(new LargeBuffer),
14 | buffers_()
15 | {
16 | currentBuffer_->bzero();
17 | nextBuffer_->bzero();
18 | buffers_.reserve(16); // Only maintain queue length of 2~16
19 | }
20 | // This function is called to handle the process where frontend passes LOG_XXX<<"..." to backend, and backend writes log messages to log file
21 | void AsyncLogging::append(const char *logline, int len)
22 | {
23 | std::lock_guard lg(mutex_);
24 | // Buffer has enough space to write
25 | if (currentBuffer_->avail() > static_cast(len))
26 | {
27 | currentBuffer_->append(logline, len);
28 | }
29 | else
30 | {
31 | buffers_.push_back(std::move(currentBuffer_));
32 |
33 | if (nextBuffer_)
34 | {
35 | currentBuffer_ = std::move(nextBuffer_);
36 | }
37 | else
38 | {
39 | currentBuffer_.reset(new LargeBuffer);
40 | }
41 | currentBuffer_->append(logline, len);
42 | // Wake up backend thread to write to disk
43 | cond_.notify_one();
44 | }
45 | }
46 |
47 | void AsyncLogging::threadFunc()
48 | {
49 | // output interface for writing to disk
50 | LogFile output(basename_, rollSize_);
51 | BufferPtr newbuffer1(new LargeBuffer); // Create new buffer to replace currentbuffer_
52 | BufferPtr newbuffer2(new LargeBuffer); // Create new buffer2 to replace newBuffer_, to prevent frontend from being unable to write when backend buffers are full
53 | newbuffer1->bzero();
54 | newbuffer2->bzero();
55 | // Buffer array set to 16, used to swap with frontend buffer array
56 | BufferVector buffersToWrite;
57 | buffersToWrite.reserve(16);
58 | while (running_)
59 | {
60 | {
61 | // Mutex protection ensures other frontend threads cannot write to frontend buffer
62 | std::unique_lock lg(mutex_);
63 | if (buffers_.empty())
64 | {
65 | cond_.wait_for(lg, std::chrono::seconds(3));
66 | }
67 | buffers_.push_back(std::move(currentBuffer_));
68 | currentBuffer_ = std::move(newbuffer1);
69 | if (!nextBuffer_)
70 | {
71 | nextBuffer_ = std::move(newbuffer2);
72 | }
73 | buffersToWrite.swap(buffers_);
74 | }
75 | // Take data from write buffer and write to disk through LogFile interface
76 | for (auto &buffer : buffersToWrite)
77 | {
78 | output.append(buffer->data(), buffer->length());
79 | }
80 |
81 | if (buffersToWrite.size() > 2)
82 | {
83 | buffersToWrite.resize(2);
84 | }
85 |
86 | if (!newbuffer1)
87 | {
88 | newbuffer1 = std::move(buffersToWrite.back());
89 | buffersToWrite.pop_back();
90 | newbuffer1->reset();
91 | }
92 | if (!newbuffer2)
93 | {
94 | newbuffer2 = std::move(buffersToWrite.back());
95 | buffersToWrite.pop_back();
96 | newbuffer2->reset();
97 | }
98 | buffersToWrite.clear(); // Clear backend buffer queue
99 | output.flush(); // Clear file buffer
100 | }
101 | output.flush(); // Ensure everything is flushed
102 | }
--------------------------------------------------------------------------------
/log/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Get all source files in current directory
2 | file(GLOB LOG_FILE ${CMAKE_CURRENT_SOURCE_DIR}/*cc)
3 |
4 | # Create static or shared library
5 | add_library(log_lib SHARED ${LOG_FILE})
--------------------------------------------------------------------------------
/log/CurrentThread.cc:
--------------------------------------------------------------------------------
1 | #include "CurrentThread.h"
2 |
3 | namespace CurrentThread
4 | {
5 | thread_local int t_cachedTid = 0; // Define thread local variable in source file
6 | void cacheTid()
7 | {
8 | if (t_cachedTid == 0)
9 | {
10 | t_cachedTid = static_cast(::syscall(SYS_gettid)); // Ensure syscall and SYS_gettid are defined
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/log/FileUtil.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include "FileUtil.h"
3 |
4 | FileUtil::FileUtil(std::string &file_name) : file_(::fopen(file_name.c_str(), "ae")),
5 | writtenBytes_(0)
6 | {
7 | // Set file_ buffer to local buffer to reduce IO operations
8 | ::setbuffer(file_, buffer_, sizeof(buffer_));
9 | }
10 | FileUtil::~FileUtil()
11 | {
12 | if (file_)
13 | {
14 | ::fclose(file_);
15 | }
16 | }
17 | // Write data to file
18 | void FileUtil::append(const char *data, size_t len)
19 | {
20 | size_t writen = 0;
21 | while (writen != len)
22 | {
23 | size_t remain = len - writen;
24 | size_t n = write(data + writen, remain);
25 | if (n != remain)
26 | {
27 | // Error checking
28 | int err = ferror(file_);
29 | if (err)
30 | {
31 | fprintf(stderr, "AppendFile::append() failed %s\n", strerror(err));
32 | clearerr(file_); // Clear file pointer error flag
33 | break;
34 | }
35 | }
36 | writen += n;
37 | }
38 | writtenBytes_ += writen;
39 | }
40 |
41 | void FileUtil::flush()
42 | {
43 | ::fflush(file_);
44 | }
45 | // Actually write data to file
46 | size_t FileUtil::write(const char *data, size_t len)
47 | {
48 | // Using non-thread-safe fwrite() for performance reasons
49 | return ::fwrite_unlocked(data, 1, len, file_);
50 | }
--------------------------------------------------------------------------------
/log/LogFile.cc:
--------------------------------------------------------------------------------
1 | #include "LogFile.h"
2 | LogFile::LogFile(const std::string &basename,
3 | off_t rollsize,
4 | int flushInterval,
5 | int checkEveryN ) : basename_(basename),
6 | rollsize_(rollsize),
7 | flushInterval_(flushInterval),
8 | checkEveryN_(checkEveryN),
9 | startOfPeriod_(0),
10 | lastRoll_(0),
11 | lastFlush_(0)
12 | {
13 | // When restarting, there might not be a log file, so when constructing the logFile object, directly call rollfile() to create a new log file
14 | rollFile();
15 | }
16 | LogFile::~LogFile() = default;
17 | void LogFile::append(const char *data, int len)
18 | {
19 | std::lock_guard lg(mutex_);
20 | appendInlock(data, len);
21 | }
22 | void LogFile::flush()
23 | {
24 | file_->flush();
25 | }
26 | // Roll log file
27 | bool LogFile::rollFile()
28 | {
29 | time_t now = 0;
30 | std::string filename = getLogFileName(basename_, &now);
31 | time_t start = now / kRollPerSeconds_ * kRollPerSeconds_;
32 | if (now > lastRoll_)
33 | {
34 | lastFlush_ = now;
35 | lastRoll_ = now;
36 | startOfPeriod_ = start;
37 | // Make file_ point to a file named filename, equivalent to creating a new file, but each rollfile() call creates a new file object to write data to the log file
38 | file_.reset(new FileUtil(filename));
39 | return true;
40 | }
41 | return false;
42 | }
43 | // Log format: basename+now+".log"
44 | std::string LogFile::getLogFileName(const std::string &basename, time_t *now)
45 | {
46 | std::string filename;
47 | filename.reserve(basename.size() + 64);
48 | filename = basename;
49 |
50 | char timebuf[32];
51 | struct tm tm;
52 | *now = time(NULL); // Get current time
53 | localtime_r(now, &tm);
54 | strftime(timebuf, sizeof(timebuf), ".%Y%m%d-%H%M%S", &tm);
55 |
56 | filename += timebuf;
57 | filename += ".log";
58 | return filename;
59 | }
60 | void LogFile::appendInlock(const char *data, int len)
61 | {
62 | file_->append(data, len);
63 |
64 | time_t now = time(NULL); // Current time
65 | ++count_;
66 |
67 | // 1. Check if log rolling is needed
68 | if (file_->writtenBytes() > rollsize_)
69 | {
70 | rollFile();
71 | }
72 | else if (count_ >= checkEveryN_) // After reaching write count threshold, perform check
73 | {
74 | count_ = 0;
75 |
76 | // Roll log based on time period
77 | time_t thisPeriod = now / kRollPerSeconds_ * kRollPerSeconds_;
78 | if (thisPeriod != startOfPeriod_)
79 | {
80 | rollFile();
81 | }
82 | }
83 |
84 | // 2. Check if log needs to be flushed (independent flush logic)
85 | if (now - lastFlush_ > flushInterval_)
86 | {
87 | lastFlush_ = now;
88 | file_->flush();
89 | }
90 | }
91 |
92 |
--------------------------------------------------------------------------------
/log/LogStream.cc:
--------------------------------------------------------------------------------
1 | #include "LogStream.h"
2 | #include
3 |
4 | static const char digits[] = "9876543210123456789";
5 |
6 | template
7 | void LogStream::formatInteger(T num)
8 | {
9 | if (buffer_.avail() >= kMaxNumberSize)
10 | {
11 | char *start = buffer_.current();
12 | char *cur = start;
13 | static const char *zero = digits + 9;
14 | bool negative = (num < 0); // Check if num is negative
15 | do
16 | {
17 | int remainder = static_cast(num % 10);
18 | (*cur++) = zero[remainder];
19 | num /= 10;
20 | } while (num != 0);
21 | if (negative)
22 | {
23 | *cur++ = '-';
24 | }
25 | *cur = '\0';
26 | std::reverse(start, cur);
27 | int length = static_cast(cur - start);
28 | buffer_.add(length);
29 | }
30 | }
31 | // Overload the output stream operator << to write boolean values into the buffer
32 | LogStream &LogStream::operator<<(bool express) {
33 | buffer_.append(express ? "true" : "false", express ? 4 : 5);
34 | return *this;
35 | }
36 |
37 | // Overload the output stream operator << to write short integers into the buffer
38 | LogStream &LogStream::operator<<(short number) {
39 | formatInteger(number);
40 | return *this;
41 | }
42 |
43 | // Overload the output stream operator << to write unsigned short integers into the buffer
44 | LogStream &LogStream::operator<<(unsigned short number) {
45 | formatInteger(number);
46 | return *this;
47 | }
48 |
49 | // Overload the output stream operator << to write integers into the buffer
50 | LogStream &LogStream::operator<<(int number) {
51 | formatInteger(number);
52 | return *this;
53 | }
54 |
55 | // Overload the output stream operator << to write unsigned integers into the buffer
56 | LogStream &LogStream::operator<<(unsigned int number) {
57 | formatInteger(number);
58 | return *this;
59 | }
60 |
61 | // Overload the output stream operator << to write long integers into the buffer
62 | LogStream &LogStream::operator<<(long number) {
63 | formatInteger(number);
64 | return *this;
65 | }
66 |
67 | // Overload the output stream operator << to write unsigned long integers into the buffer
68 | LogStream &LogStream::operator<<(unsigned long number) {
69 | formatInteger(number);
70 | return *this;
71 | }
72 |
73 | // Overload the output stream operator << to write long long integers into the buffer
74 | LogStream &LogStream::operator<<(long long number) {
75 | formatInteger(number);
76 | return *this;
77 | }
78 |
79 | // Overload the output stream operator << to write unsigned long long integers into the buffer
80 | LogStream &LogStream::operator<<(unsigned long long number) {
81 | formatInteger(number);
82 | return *this;
83 | }
84 |
85 | // Overload the output stream operator << to write floating point numbers into the buffer
86 | LogStream &LogStream::operator<<(float number) {
87 | *this<(number);
88 | return *this;
89 | }
90 |
91 | // Overload the output stream operator << to write double precision floating point numbers into the buffer
92 | LogStream &LogStream::operator<<(double number) {
93 | char buffer[32];
94 | snprintf(buffer, sizeof(buffer), "%.12g", number);
95 | buffer_.append(buffer, strlen(buffer));
96 | return *this;
97 | }
98 |
99 | // Overload the output stream operator << to write characters into the buffer
100 | LogStream &LogStream::operator<<(char str) {
101 | buffer_.append(&str, 1);
102 | return *this;
103 | }
104 |
105 | // Overload the output stream operator << to write C-style character strings into the buffer
106 | LogStream &LogStream::operator<<(const char *str) {
107 | buffer_.append(str, strlen(str));
108 | return *this;
109 | }
110 |
111 | // Overload the output stream operator << to write unsigned character pointers into the buffer
112 | LogStream &LogStream::operator<<(const unsigned char *str) {
113 | buffer_.append(reinterpret_cast(str), strlen(reinterpret_cast(str)));
114 | return *this;
115 | }
116 |
117 | // Overload the output stream operator << to write std::string objects into the buffer
118 | LogStream &LogStream::operator<<(const std::string &str) {
119 | buffer_.append(str.c_str(), str.size());
120 | return *this;
121 | }
122 |
123 | LogStream& LogStream::operator<<(const GeneralTemplate& g)
124 | {
125 | buffer_.append(g.data_, g.len_);
126 | return *this;
127 | }
--------------------------------------------------------------------------------
/memory/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Get all source files in current directory
2 | file(GLOB MEMORY_FILE ${CMAKE_CURRENT_SOURCE_DIR}/*cc)
3 |
4 | # Create static or shared library
5 | add_library(memory_lib SHARED ${MEMORY_FILE})
--------------------------------------------------------------------------------
/memory/memoryPool.cc:
--------------------------------------------------------------------------------
1 | #include "memoryPool.h"
2 |
3 | namespace memoryPool
4 | {
5 | MemoryPool::MemoryPool(size_t BlockSize)
6 | : BlockSize_ (BlockSize)
7 | {}
8 |
9 | MemoryPool::~MemoryPool()
10 | {
11 | // Delete consecutive blocks
12 | Slot* cur = firstBlock_;
13 | while (cur)
14 | {
15 | Slot* next = cur->next;
16 | // Equivalent to free(reinterpret_cast(firstBlock_));
17 | // Convert to void pointer, because void type doesn't need to call destructor, only free space
18 | operator delete(reinterpret_cast(cur));
19 | cur = next;
20 | }
21 | }
22 |
23 | void MemoryPool::init(size_t size)
24 | {
25 | assert(size > 0);
26 | SlotSize_ = size;
27 | firstBlock_ = nullptr;
28 | curSlot_ = nullptr;
29 | freeList_ = nullptr;
30 | lastSlot_ = nullptr;
31 | }
32 |
33 | void* MemoryPool::allocate()
34 | {
35 | // Prioritize using memory slots from the free list
36 | if (freeList_ != nullptr)
37 | {
38 | {
39 | std::lock_guard lock(mutexForFreeList_);
40 | if (freeList_ != nullptr)
41 | {
42 | Slot* temp = freeList_;
43 | freeList_ = freeList_->next;
44 | return temp;
45 | }
46 | }
47 | }
48 |
49 | Slot* temp;
50 | {
51 | std::lock_guard lock(mutexForBlock_);
52 | if (curSlot_ >= lastSlot_)
53 | {
54 | // Current memory block has no available slots, allocate a new memory block
55 | allocateNewBlock();
56 | }
57 |
58 | temp = curSlot_;
59 | // Cannot directly do curSlot_ += SlotSize_ here because curSlot_ is of type Slot*, so need to divide by SlotSize_ and add 1
60 | curSlot_ += SlotSize_ / sizeof(Slot);
61 | }
62 |
63 | return temp;
64 | }
65 |
66 | void MemoryPool::deallocate(void* ptr)
67 | {
68 | if (ptr)
69 | {
70 | // Recycle memory, insert memory into free list through head insertion
71 | std::lock_guard lock(mutexForFreeList_);
72 | reinterpret_cast(ptr)->next = freeList_;
73 | freeList_ = reinterpret_cast(ptr);
74 | }
75 | }
76 |
77 | void MemoryPool::allocateNewBlock()
78 | {
79 | //std::cout << "Apply for a memory block, SlotSize: " << SlotSize_ << std::endl;
80 | // Insert new memory block using head insertion
81 | void* newBlock = operator new(BlockSize_);
82 | reinterpret_cast(newBlock)->next = firstBlock_;
83 | firstBlock_ = reinterpret_cast(newBlock);
84 |
85 | char* body = reinterpret_cast(newBlock) + sizeof(Slot*);
86 | size_t paddingSize = padPointer(body, SlotSize_); // Calculate padding size needed for alignment
87 | curSlot_ = reinterpret_cast(body + paddingSize);
88 |
89 | // If exceeding this mark position, it means the memory block has no available slots, need to request a new memory block from the system
90 | lastSlot_ = reinterpret_cast(reinterpret_cast(newBlock) + BlockSize_ - SlotSize_ + 1);
91 |
92 | freeList_ = nullptr;
93 | }
94 |
95 | // Align pointer to multiple of slot size
96 | size_t MemoryPool::padPointer(char* p, size_t align)
97 | {
98 | // align is the slot size
99 | return (align - reinterpret_cast(p)) % align;
100 | }
101 |
102 | void HashBucket::initMemoryPool()
103 | {
104 | for (int i = 0; i < MEMORY_POOL_NUM; i++)
105 | {
106 | getMemoryPool(i).init((i + 1) * SLOT_BASE_SIZE);
107 | }
108 | }
109 |
110 | // Singleton pattern
111 | MemoryPool& HashBucket::getMemoryPool(int index)
112 | {
113 | static MemoryPool memoryPool[MEMORY_POOL_NUM];
114 | return memoryPool[index];
115 | }
116 |
117 | } // namespace memoryPool
118 |
--------------------------------------------------------------------------------
/src/Acceptor.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 |
9 | static int createNonblocking()
10 | {
11 | int sockfd = ::socket(AF_INET, SOCK_STREAM | SOCK_NONBLOCK | SOCK_CLOEXEC, IPPROTO_TCP);
12 | if (sockfd < 0)
13 | {
14 | LOG_FATAL << "listen socket create err " << errno;
15 | }
16 | return sockfd;
17 | }
18 |
19 | Acceptor::Acceptor(EventLoop *loop, const InetAddress &listenAddr, bool reuseport)
20 | : loop_(loop)
21 | , acceptSocket_(createNonblocking())
22 | , acceptChannel_(loop, acceptSocket_.fd())
23 | , listenning_(false)
24 | {
25 | acceptSocket_.setReuseAddr(true);
26 | acceptSocket_.setReusePort(true);
27 | acceptSocket_.bindAddress(listenAddr);
28 | // TcpServer::start() => Acceptor.listen() If there is a new user connection, execute a callback (accept => connfd => package into Channel => wake up subloop)
29 | // baseloop detects an event => acceptChannel_(listenfd) => execute this callback function
30 | acceptChannel_.setReadCallback(
31 | std::bind(&Acceptor::handleRead, this));
32 | }
33 |
34 | Acceptor::~Acceptor()
35 | {
36 | acceptChannel_.disableAll(); // Remove interested events from Poller
37 | acceptChannel_.remove(); // Call EventLoop->removeChannel => Poller->removeChannel to remove the corresponding part from Poller's ChannelMap
38 | }
39 |
40 | void Acceptor::listen()
41 | {
42 | listenning_ = true;
43 | acceptSocket_.listen(); // listen
44 | acceptChannel_.enableReading(); // Register acceptChannel_ to Poller !Important
45 | }
46 |
47 | // listenfd has an event, meaning there is a new user connection
48 | void Acceptor::handleRead()
49 | {
50 | InetAddress peerAddr;
51 | int connfd = acceptSocket_.accept(&peerAddr);
52 | if (connfd >= 0)
53 | {
54 | if (NewConnectionCallback_)
55 | {
56 | NewConnectionCallback_(connfd, peerAddr); // Poll to find subLoop, wake up and distribute the current new client's Channel
57 | }
58 | else
59 | {
60 | ::close(connfd);
61 | }
62 | }
63 | else
64 | {
65 | LOG_ERROR<<"accept Err";
66 | if (errno == EMFILE)
67 | {
68 | LOG_ERROR<<"sockfd reached limit";
69 | }
70 | }
71 | }
--------------------------------------------------------------------------------
/src/Buffer.cc:
--------------------------------------------------------------------------------
1 | #include
2 | #include
3 | #include
4 |
5 | #include
6 |
7 | /**
8 | * Read data from fd, Poller works in LT mode
9 | * Buffer has a size limit! But when reading data from fd, we don't know the final size of the TCP data
10 | *
11 | * @description: The method to read from socket to buffer uses readv to read into buffer_ first,
12 | * If buffer_ space is not enough, it will read into a 65536-byte space on the stack, then append
13 | * to buffer_. This approach avoids the overhead of system calls while not affecting data reception.
14 | **/
15 | ssize_t Buffer::readFd(int fd, int *saveErrno)
16 | {
17 | // Extra stack space, used when reading from socket and buffer_ is temporarily insufficient,
18 | // temporarily store data until buffer_ is reallocated with enough space, then swap data to buffer_.
19 | char extrabuf[65536] = {0}; // Stack memory space 65536/1024 = 64KB
20 |
21 | /*
22 | struct iovec {
23 | ptr_t iov_base; // The buffer pointed to by iov_base stores the data received by readv or the data to be sent by writev
24 | size_t iov_len; // In different situations, iov_len determines the maximum length to receive or the actual length to write
25 | };
26 | */
27 |
28 | // Use iovec to allocate two consecutive buffers
29 | struct iovec vec[2];
30 | const size_t writable = writableBytes(); // This is the remaining writable space in the buffer
31 |
32 | // The first buffer, points to writable space
33 | vec[0].iov_base = begin() + writerIndex_;
34 | vec[0].iov_len = writable;
35 | // The second buffer, points to stack space
36 | vec[1].iov_base = extrabuf;
37 | vec[1].iov_len = sizeof extrabuf;
38 |
39 | // when there is enough space in this buffer, don't read into extrabuf.
40 | // when extrabuf is used, we read 128k-1 bytes at most.
41 | // The reason for saying at most 128k-1 bytes is: if writable is 64k-1, then two buffers are needed, the first is 64k-1, the second is 64k, so at most 128k-1
42 | // If the first buffer >= 64k, then only one buffer is used and the stack space extrabuf[65536] is not used
43 | const int iovcnt = (writable < sizeof extrabuf) ? 2 : 1;
44 | const ssize_t n = ::readv(fd, vec, iovcnt);
45 |
46 | if (n < 0)
47 | {
48 | *saveErrno = errno;
49 | }
50 | else if (n <= writable) // Buffer is enough
51 | {
52 | writerIndex_ += n;
53 | }
54 | else // extrabuf is also written
55 | {
56 | writerIndex_ = buffer_.size();
57 | append(extrabuf, n - writable); // writerIndex_ begins to write from buffer_.size()
58 | }
59 | return n;
60 | }
61 |
62 | // inputBuffer_.readFd reads data from the peer into inputBuffer_, moving the writerIndex_ pointer
63 | // outputBuffer_.writeFd writes data to outputBuffer_, starting from readerIndex_, can write readableBytes() bytes
64 | /**
65 | * @description: The method to write data from buffer to fd uses writev
66 | * @param {int} fd - Socket file descriptor
67 | * @param {int} *saveErrno - Error number
68 | * @return {ssize_t} - Number of bytes written
69 | */
70 | ssize_t Buffer::writeFd(int fd, int *saveErrno)
71 | {
72 | ssize_t n = ::write(fd, peek(), readableBytes());
73 | if (n < 0)
74 | {
75 | *saveErrno = errno;
76 | }
77 | return n;
78 | }
--------------------------------------------------------------------------------
/src/CMakeLists.txt:
--------------------------------------------------------------------------------
1 | # Get all source files in current directory
2 | file(GLOB SRC_FILE ${CMAKE_CURRENT_SOURCE_DIR}/*cc)
3 | list(REMOVE_ITEM SRC_FILE ${CMAKE_CURRENT_SOURCE_DIR}/main.cc)
4 |
5 | # Create shared library
6 | add_library(src_lib SHARED ${SRC_FILE})
7 |
8 | # Create executable
9 | add_executable(main main.cc)
10 |
11 | # Link necessary libraries
12 | target_link_libraries(main src_lib memory_lib log_lib ${LIBS})
13 |
14 |
--------------------------------------------------------------------------------
/src/Channel.cc:
--------------------------------------------------------------------------------
1 | #include
2 |
3 | #include
4 | #include
5 | #include
6 |
7 | const int Channel::kNoneEvent = 0; // No event
8 | const int Channel::kReadEvent = EPOLLIN | EPOLLPRI; // Read event
9 | const int Channel::kWriteEvent = EPOLLOUT; // Write event
10 |
11 | // EventLoop: ChannelList Poller
12 | Channel::Channel(EventLoop *loop, int fd)
13 | : loop_(loop)
14 | , fd_(fd)
15 | , events_(0)
16 | , revents_(0)
17 | , index_(-1)
18 | , tied_(false)
19 | {
20 | }
21 |
22 | Channel::~Channel()
23 | {
24 | }
25 |
26 | // When is the channel's tie method called? TcpConnection => channel
27 | /*
28 | * TcpConnection registers the callback functions corresponding to Channel, and the passed-in callback functions are all member methods of TcpConnection.
29 | * Therefore, it can be said that: Channel's end must be later than TcpConnection object!
30 | * Here, tie is used to solve the lifetime issue between TcpConnection and Channel, ensuring that the Channel object can be destroyed before TcpConnection is destroyed.
31 | */
32 | void Channel::tie(const std::shared_ptr &obj)
33 | {
34 | tie_ = obj;
35 | tied_ = true;
36 | }
37 | //update and remove => EpollPoller updates the channel's state in poller
38 | /**
39 | * When the events of the channel represented by fd change, update is responsible for updating the corresponding events of fd in poller
40 | **/
41 | void Channel::update()
42 | {
43 | // Through the eventloop to which the channel belongs, call the corresponding method of poller to register the events of fd
44 | loop_->updateChannel(this);
45 | }
46 |
47 | // Remove the current channel from the eventloop to which it belongs
48 | void Channel::remove()
49 | {
50 | loop_->removeChannel(this);
51 | }
52 |
53 | void Channel::handleEvent(Timestamp receiveTime)
54 | {
55 | if (tied_)
56 | {
57 | std::shared_ptr