/src/tarantool/src/lua/alloc.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * SPDX-License-Identifier: BSD-2-Clause |
3 | | * |
4 | | * Copyright 2010-2024, Tarantool AUTHORS, please see AUTHORS file. |
5 | | */ |
6 | | #include "lua/utils.h" |
7 | | |
8 | | #include <lauxlib.h> /* luaL_error */ |
9 | | |
10 | | #define LUA_MEMORY_LIMIT_DEFAULT (2ULL * 1024 * 1024 * 1024) |
11 | | |
12 | | /** |
13 | | * Default allocator function which is wrapped into a new one |
14 | | * with the Lua memory limit checker. |
15 | | */ |
16 | | static lua_Alloc orig_alloc; |
17 | | /** |
18 | | * Memory limit for LuaJIT. |
19 | | */ |
20 | | static size_t memory_limit = LUA_MEMORY_LIMIT_DEFAULT; |
21 | | /** |
22 | | * Amount of memory used by LuaJIT. |
23 | | */ |
24 | | static size_t used; |
25 | | |
26 | | /** |
27 | | * Lua custom memory allocation function. It extends the original |
28 | | * one with a memory counter and a limit check. |
29 | | */ |
30 | | static void * |
31 | | alloc_with_limit(void *ud, void *ptr, size_t osize, size_t nsize) |
32 | 0 | { |
33 | 0 | size_t new_used = used + nsize - osize; |
34 | 0 | if (new_used > memory_limit) { |
35 | | /* |
36 | | * Returning NULL results in the "not enough |
37 | | * memory" error. |
38 | | */ |
39 | 0 | return NULL; |
40 | 0 | } |
41 | | |
42 | 0 | void *result = orig_alloc(ud, ptr, osize, nsize); |
43 | |
|
44 | 0 | if (result != NULL || nsize == 0) |
45 | 0 | used = new_used; |
46 | | |
47 | | /* |
48 | | * Result may be NULL, in this case "not enough memory" |
49 | | * is raised. |
50 | | */ |
51 | 0 | return result; |
52 | 0 | } |
53 | | |
54 | | static int |
55 | | set_alloc_limit(size_t new_memory_limit) |
56 | 0 | { |
57 | 0 | if (new_memory_limit < used) { |
58 | 0 | diag_set(LuajitError, "Cannot limit the Lua memory with values " |
59 | 0 | "less than the currently allocated amount"); |
60 | 0 | return -1; |
61 | 0 | } |
62 | 0 | memory_limit = new_memory_limit; |
63 | 0 | return 0; |
64 | 0 | } |
65 | | |
66 | | void |
67 | | luaT_initalloc(struct lua_State *L) |
68 | 0 | { |
69 | 0 | assert(L != NULL); |
70 | | |
71 | 0 | used = luaL_getgctotal(L); |
72 | |
|
73 | 0 | void *orig_ud; |
74 | 0 | orig_alloc = lua_getallocf(L, &orig_ud); |
75 | |
|
76 | 0 | assert(orig_alloc != NULL); |
77 | | |
78 | 0 | lua_setallocf(L, alloc_with_limit, orig_ud); |
79 | 0 | } |
80 | | |
81 | | /** |
82 | | * alloc.getlimit() -- get the allocator memory limit. |
83 | | */ |
84 | | static int |
85 | | lbox_alloc_getlimit(struct lua_State *L) |
86 | 0 | { |
87 | 0 | lua_pushinteger(L, memory_limit); |
88 | 0 | return 1; |
89 | 0 | } |
90 | | |
91 | | /** |
92 | | * alloc.setlimit() -- set the allocator memory limit. |
93 | | * Returns old memory limit. |
94 | | */ |
95 | | static int |
96 | | lbox_alloc_setlimit(struct lua_State *L) |
97 | 0 | { |
98 | 0 | if (lua_gettop(L) < 1) { |
99 | 0 | diag_set(IllegalParams, "Usage: alloc.setlimit(amount)"); |
100 | 0 | return luaT_error(L); |
101 | 0 | } |
102 | | |
103 | 0 | ssize_t amount = luaL_checkinteger(L, 1); |
104 | |
|
105 | 0 | if (amount < 0) { |
106 | 0 | diag_set(IllegalParams, "Invalid memory limit: " |
107 | 0 | "the value must be >= 0"); |
108 | 0 | return luaT_error(L); |
109 | 0 | } |
110 | | |
111 | 0 | size_t old_memory_limit = memory_limit; |
112 | |
|
113 | 0 | if (set_alloc_limit(amount) < 0) |
114 | 0 | return luaT_error(L); |
115 | | |
116 | 0 | lua_pushinteger(L, old_memory_limit); |
117 | 0 | return 1; |
118 | 0 | } |
119 | | |
120 | | /** |
121 | | * alloc.used() -- get the amount of the allocated memory. |
122 | | */ |
123 | | static int |
124 | | lbox_alloc_used(struct lua_State *L) |
125 | 0 | { |
126 | 0 | lua_pushinteger(L, used); |
127 | 0 | return 1; |
128 | 0 | } |
129 | | |
130 | | /** |
131 | | * alloc.unused() -- get the amount of the unused memory. |
132 | | */ |
133 | | static int |
134 | | lbox_alloc_unused(struct lua_State *L) |
135 | 0 | { |
136 | 0 | lua_pushinteger(L, memory_limit - used); |
137 | 0 | return 1; |
138 | 0 | } |
139 | | |
140 | | void |
141 | | tarantool_lua_alloc_init(struct lua_State *L) |
142 | 0 | { |
143 | 0 | static const struct luaL_Reg alloc_methods[] = { |
144 | 0 | {"setlimit", lbox_alloc_setlimit}, |
145 | 0 | {"getlimit", lbox_alloc_getlimit}, |
146 | 0 | {"used", lbox_alloc_used}, |
147 | 0 | {"unused", lbox_alloc_unused}, |
148 | 0 | {NULL, NULL} |
149 | 0 | }; |
150 | |
|
151 | 0 | luaT_newmodule(L, "internal.alloc", alloc_methods); |
152 | 0 | lua_pop(L, 1); |
153 | 0 | } |