History

Basic implementation of Sys module functions - Sys.create, Sys.open, Sys.read, Sys.write - Sys.tokenize

Code analysis violations Bug in scheduler - race during with worker thread shutdown.

arr committed 10 years ago

Commit 992a33f1 parent 6d77c2d18 files · +695 −243Browse files

Showing the changes to src/vm/sys/Sysmod.cpp (1 of 18 files). Show all

Unified · Split

src/vm/sys/Sysmod.cpp+375−97
@@ -5,6 +5,7 @@ · 4 unmodified lines
55 //
66
77 #include <array>
8+#include <algorithm>
89 #include <chrono>
910 #include <thread>
1011 #include <iostream>
@@ -13,6 +14,7 @@ · 2 unmodified lines
1314 #include <exceptions.h>
1415 #include <debug.h>
1516 #include <utf8.h>
17+#include <vm_memory.h>
1618 #include "Sysmod.h"
1719 #include "sys_utils.h"
1820
@@ -69,99 +71,153 @@ namespace · 50 unmodified lines
6971 };
7072
7173 const word_t Sysmodlen = 43;
72−}
7374
74−std::shared_ptr<const type_descriptor_t> T_Qid;
75−std::shared_ptr<const type_descriptor_t> T_Dir;
76−std::shared_ptr<const type_descriptor_t> T_FD;
77−std::shared_ptr<const type_descriptor_t> T_Connection;
78−std::shared_ptr<const type_descriptor_t> T_FileIO;
75+ std::shared_ptr<const type_descriptor_t> T_Qid;
76+ std::shared_ptr<const type_descriptor_t> T_Dir;
77+ std::shared_ptr<const type_descriptor_t> T_FD;
78+ std::shared_ptr<const type_descriptor_t> T_Connection;
79+ std::shared_ptr<const type_descriptor_t> T_FileIO;
7980
80−struct vm_fd_t final : public Sys_FD
81−{
82− static void finalizer(vm_alloc_t *fileHandle)
81+ class vm_fd_t final : public Sys_FD
8382 {
84− auto fd = fileHandle->get_allocation<vm_fd_t>();
85− fd->~vm_fd_t();
86− }
83+ public: // static
84+ static void finalizer(vm_alloc_t *fileHandle)
85+ {
86+ auto fd = fileHandle->get_allocation<vm_fd_t>();
87+
88+ if (debug::is_component_tracing_enabled<debug::component_trace_t::builtin>())
89+ {
90+ debug::log_msg(
91+ debug::component_trace_t::builtin,
92+ debug::log_level_t::debug,
93+ "sys: fd: finalize: %d\n",
94+ fd->fd);
95+ }
96+
97+ sys::drop_fd_record(fd->fd);
98+ fd->~vm_fd_t();
99+ }
87100
88− vm_fd_t(word_t fd, std::unique_ptr<std::iostream> s)
89− : Sys_FD{ fd }
90− , _istream{ s.get() }
91− , _ostream{ s.get() }
92− {
93− assert(s != nullptr);
94− _stream = std::move(s);
95− }
101+ static std::tuple<word_t, vm_alloc_t *> create(std::unique_ptr<std::iostream> s, word_t fd_mode, vm_string_t *fd_path)
102+ {
103+ assert(s != nullptr);
104+ auto alloc = vm_alloc_t::allocate(T_FD);
105+ const auto fd = sys::create_fd_record(alloc);
106+ auto vm_fd = ::new(alloc->get_allocation())vm_fd_t{ fd, std::move(s), fd_mode, fd_path };
96107
97− vm_fd_t(word_t fd, std::unique_ptr<std::istream> s)
98− : Sys_FD{ fd }
99− , _istream{ s.get() }
100− , _ostream{ nullptr }
101− {
102− assert(s != nullptr);
103− _stream = std::move(s);
104− }
108+ return std::make_tuple(vm_fd->fd, alloc);
109+ }
105110
106− vm_fd_t(word_t fd, std::unique_ptr<std::ostream> s)
107− : Sys_FD{ fd }
108− , _istream{ nullptr }
109− , _ostream{ s.get() }
110− {
111− assert(s != nullptr);
112− _stream = std::move(s);
113− }
111+ private:
112+ vm_fd_t(const word_t fd, std::unique_ptr<std::iostream> s, const word_t fd_mode, vm_string_t *fd_path)
113+ : Sys_FD{ fd }
114+ , _fd_mode{ fd_mode }
115+ , _fd_path{ fd_path }
116+ , _istream{ s.get() }
117+ , _ostream{ s.get() }
118+ {
119+ _stream = std::move(s);
114120
115− vm_fd_t(word_t fd, std::istream &s)
116− : Sys_FD{ fd }
117− , _istream{ &s }
118− , _ostream{ nullptr }
119− {
120− }
121+ if (_fd_path != nullptr)
122+ _fd_path->add_ref();
123+ }
121124
122− vm_fd_t(word_t fd, std::ostream &s)
123− : Sys_FD{ fd }
124− , _istream{ nullptr }
125− , _ostream{ &s }
126− {
127− }
125+ public:
126+ vm_fd_t(const word_t fd, std::istream &is)
127+ : Sys_FD{ fd }
128+ , _fd_mode{}
129+ , _fd_path{ nullptr }
130+ , _istream{ &is }
131+ , _ostream{ nullptr }
132+ {
133+ }
128134
129− ~vm_fd_t()
130− {
131− debug::assign_debug_pointer(&_istream);
132− debug::assign_debug_pointer(&_ostream);
135+ vm_fd_t(const word_t fd, std::ostream &os)
136+ : Sys_FD{ fd }
137+ , _fd_mode{}
138+ , _fd_path{ nullptr }
139+ , _istream{ nullptr }
140+ , _ostream{ &os }
141+ {
142+ }
133143
134− if (debug::is_component_tracing_enabled<debug::component_trace_t::memory>())
135− debug::log_msg(debug::component_trace_t::memory, debug::log_level_t::debug, "destroy: file descriptor: %d", fd);
136− }
144+ ~vm_fd_t()
145+ {
146+ debug::assign_debug_pointer(&_istream);
147+ debug::assign_debug_pointer(&_ostream);
148+ if (_stream != nullptr)
149+ _stream.reset();
137150
138− std::istream& input() const
139− {
140− if (_istream == nullptr)
141− throw vm_user_exception{ "File descriptor not open for write" };
151+ // Honor the close flag
152+ if ((_fd_mode & Sys_ORCLOSE) && _fd_path != nullptr)
153+ std::remove(_fd_path->str());
142154
143− return *_istream;
144− }
155+ dec_ref_count_and_free(_fd_path);
156+ debug::assign_debug_pointer(&_fd_path);
157+ }
158+
159+ word_t get_original_mode() const
160+ {
161+ return _fd_mode;
162+ }
163+
164+ vm_string_t *get_original_path() const
165+ {
166+ return _fd_path;
167+ }
168+
169+ word_t read(const word_t buffer_size_in_bytes, void *buffer)
170+ {
171+ if (_istream == nullptr)
172+ throw vm_system_exception{ "File descriptor not open for read operation" };
173+
174+ _istream->read(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));
175+ if (_istream->bad())
176+ throw vm_user_exception{ "Read operation error" };
145177
146− std::ostream& output() const
178+ return static_cast<word_t>(_istream->gcount());
179+ }
180+
181+ void write(const word_t buffer_size_in_bytes, void *buffer)
182+ {
183+ if (_ostream == nullptr)
184+ throw vm_system_exception{ "File descriptor not open for write operation" };
185+
186+ _ostream->write(static_cast<char *>(buffer), static_cast<const std::streamsize>(buffer_size_in_bytes));
187+ if (_ostream->bad())
188+ throw vm_user_exception{ "Write operation error" };
189+ }
190+
191+ private:
192+ const word_t _fd_mode;
193+ vm_string_t *_fd_path;
194+ std::istream *_istream;
195+ std::ostream *_ostream;
196+ std::unique_ptr<std::ios> _stream;
197+ };
198+
199+ vm_fd_t *fd_stdin = nullptr;
200+ vm_fd_t *fd_stdout = nullptr;
201+ vm_fd_t *fd_stderr = nullptr;
202+
203+ std::ios::openmode convert_to_openmode(const word_t mode)
147204 {
148− if (_ostream == nullptr)
149− throw vm_user_exception{ "File descriptor not open for read" };
205+ auto om = std::ios::openmode{};
150206
151− return *_ostream;
152− }
207+ // Lowest bit is for read/write.
208+ if (mode & Sys_OWRITE)
209+ om |= std::ios::out;
210+ else
211+ om |= std::ios::in; // Sys_OREAD
153212
154−private:
155− std::istream *_istream;
156− std::ostream *_ostream;
157− std::unique_ptr<std::ios> _stream;
158−};
213+ if (mode & Sys_ORDWR)
214+ om |= (std::ios::in | std::ios::out);
159215
160−namespace
161−{
162− vm_alloc_t *vm_stdin = nullptr;
163− vm_alloc_t *vm_stdout = nullptr;
164− vm_alloc_t *vm_stderr = nullptr;
216+ if (mode & Sys_OTRUNC)
217+ om |= std::ios::trunc;
218+
219+ return om;
220+ }
165221 }
166222
167223 void
@@ -176,14 +232,26 @@ Sysmodinit(void) · 8 unmodified lines
176232
177233 // Initialize default file descriptors
178234
179− vm_stdin = vm_alloc_t::allocate(T_FD);
180− ::new(vm_stdin->get_allocation()) vm_fd_t{ 0, std::cin };
235+ {
236+ auto vm_stdin = vm_alloc_t::allocate(T_FD);
237+ const auto fd = sys::create_fd_record(vm_stdin);
238+ fd_stdin = ::new(vm_stdin->get_allocation())vm_fd_t{ fd, std::cin };
239+ debug::log_msg(debug::component_trace_t::builtin, debug::log_level_t::debug, "sys: stdin: %d\n", fd);
240+ }
181241
182− vm_stdout = vm_alloc_t::allocate(T_FD);
183− ::new(vm_stdout->get_allocation()) vm_fd_t{ 1, std::cout };
242+ {
243+ auto vm_stdout = vm_alloc_t::allocate(T_FD);
244+ const auto fd = sys::create_fd_record(vm_stdout);
245+ fd_stdout = ::new(vm_stdout->get_allocation())vm_fd_t{ fd, std::cout };
246+ debug::log_msg(debug::component_trace_t::builtin, debug::log_level_t::debug, "sys: stdout: %d\n", fd);
247+ }
184248
185− vm_stderr = vm_alloc_t::allocate(T_FD);
186− ::new(vm_stderr->get_allocation()) vm_fd_t{ 2, std::cerr };
249+ {
250+ auto vm_stderr = vm_alloc_t::allocate(T_FD);
251+ const auto fd = sys::create_fd_record(vm_stderr);
252+ fd_stderr = ::new(vm_stderr->get_allocation())vm_fd_t{ fd, std::cerr };
253+ debug::log_msg(debug::component_trace_t::builtin, debug::log_level_t::debug, "sys: stderr: %d\n", fd);
254+ }
187255 }
188256
189257 void
@@ -260,19 +328,19 @@ Sys_char2byte(vm_registers_t &r, vm_t &vm) · 70 unmodified lines
260328
261329 const auto rune = static_cast<rune_t>(fp.c);
262330
263− static_assert(Sys_UTFmax == sizeof(rune_t), "UTF max should be same size as rune");
331+ static_assert(Sys_UTFmax == sizeof(rune), "UTF max should be same size as rune");
264332 uint8_t buffer_local[Sys_UTFmax];
265333 auto begin = buffer_local;
266334
267335 auto end = utf8::encode(rune, begin);
268336 const auto count = static_cast<word_t>(end - begin);
269337
270− // Initialize for failure.
271− *fp.ret = 0;
272−
273338 const auto final_index = n + count;
274339 if (final_index > buffer->get_length())
340+ {
341+ *fp.ret = 0;
275342 return;
343+ }
276344
277345 // Insert the bytes into the buffer
278346 for (auto i = word_t{ n }; i < final_index; ++i)
@@ -295,7 +363,41 @@ void · 16 unmodified lines
295363 Sys_create(vm_registers_t &r, vm_t &vm)
296364 {
297365 auto &fp = r.stack.peek_frame()->base<F_Sys_create>();
298− throw vm_system_exception{ "Instruction not implemented" };
366+
367+ auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
368+ const auto open_mode = convert_to_openmode(fp.mode);
369+
370+ auto cfs_flag = sys::cfs_flags_t::none;
371+ if (fp.mode & Sys_OEXCL)
372+ cfs_flag = (sys::cfs_flags_t::atomic | sys::cfs_flags_t::ensure_create);
373+
374+ // [TODO] Apply the requested permissions to the file.
375+ auto file_stream = sys::create_file_stream(path->str(), (open_mode | std::ios::binary), cfs_flag);
376+
377+ // Return null if file stream is not open.
378+ if (file_stream == nullptr)
379+ {
380+ *fp.ret = nullptr;
381+ return;
382+ }
383+
384+ vm_alloc_t *fd_alloc;
385+ auto fd = word_t{};
386+ std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path);
387+
388+ if (debug::is_component_tracing_enabled<debug::component_trace_t::builtin>())
389+ {
390+ debug::log_msg(
391+ debug::component_trace_t::builtin,
392+ debug::log_level_t::debug,
393+ "sys: create: %d >>%s<< %#x %#x\n",
394+ fd,
395+ path->str(),
396+ fp.mode,
397+ fp.perm);
398+ }
399+
400+ *fp.ret = fd_alloc->get_allocation();
299401 }
300402
301403 void
@@ -417,7 +519,36 @@ void · 115 unmodified lines
417519 Sys_open(vm_registers_t &r, vm_t &vm)
418520 {
419521 auto &fp = r.stack.peek_frame()->base<F_Sys_open>();
420− throw vm_system_exception{ "Instruction not implemented" };
522+
523+ auto path = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
524+ const auto open_mode = convert_to_openmode(fp.mode);
525+
526+ // [TODO] Handle opening devices - not just files (i.e. /dev/random, /dev/source, /proc/1234)
527+ auto file_stream = sys::create_file_stream(path->str(), (open_mode | std::ios::binary));
528+
529+ // Return null if file stream is not open.
530+ if (file_stream == nullptr)
531+ {
532+ *fp.ret = nullptr;
533+ return;
534+ }
535+
536+ vm_alloc_t *fd_alloc;
537+ auto fd = word_t{};
538+ std::tie(fd, fd_alloc) = vm_fd_t::create(std::move(file_stream), fp.mode, path);
539+
540+ if (debug::is_component_tracing_enabled<debug::component_trace_t::builtin>())
541+ {
542+ debug::log_msg(
543+ debug::component_trace_t::builtin,
544+ debug::log_level_t::debug,
545+ "sys: open: %d >>%s<< %#x\n",
546+ fd,
547+ path->str(),
548+ fp.mode);
549+ }
550+
551+ *fp.ret = fd_alloc->get_allocation();
421552 }
422553
423554 void
@@ -453,16 +584,16 @@ Sys_print(vm_registers_t &r, vm_t &vm) · 29 unmodified lines
453584
454585 auto static_buffer = std::array<char, 1024>{};
455586 auto msg_args = &fp.vargs;
456− auto written = printf_to_buffer(*str, msg_args, fp_base, static_buffer.size(), static_buffer.data());
587+ auto written = sys::printf_to_buffer(*str, msg_args, fp_base, static_buffer.size(), static_buffer.data());
457588 if (written >= 0)
458589 {
459− vm_stdout->get_allocation<vm_fd_t>()->output() << static_buffer.data();
590+ fd_stdout->write(written, static_buffer.data());
460591 }
461592 else
462593 {
463594 auto dynamic_buffer = std::vector<char>(static_buffer.size() * 2);
464− written = printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer);
465− vm_stdout->get_allocation<vm_fd_t>()->output() << static_buffer.data();
595+ written = sys::printf_to_dynamic_buffer(*str, msg_args, fp_base, dynamic_buffer);
596+ fd_stdout->write(written, dynamic_buffer.data());
466597 }
467598
468599 *fp.ret = written;
@@ -479,7 +610,26 @@ void · 10 unmodified lines
479610 Sys_read(vm_registers_t &r, vm_t &vm)
480611 {
481612 auto &fp = r.stack.peek_frame()->base<F_Sys_read>();
482− throw vm_system_exception{ "Instruction not implemented" };
613+
614+ const auto buffer = vm_alloc_t::from_allocation<vm_array_t>(fp.buf);
615+ assert(buffer->get_element_type()->size_in_bytes == intrinsic_type_desc::type<byte_t>()->size_in_bytes);
616+
617+ auto n = fp.n;
618+ if (buffer == nullptr || n <= 0)
619+ {
620+ *fp.ret = 0;
621+ return;
622+ }
623+
624+ // [SPEC] Supplying a size greater than the buffer length is
625+ // equivalent to indicating the entire buffer should be filled.
626+ n = std::min(n, buffer->get_length());
627+
628+ auto fd_alloc = vm_alloc_t::from_allocation(fp.fd);
629+ assert(fd_alloc->alloc_type == T_FD);
630+
631+ auto fd = fd_alloc->get_allocation<vm_fd_t>();
632+ *fp.ret = fd->read(n, buffer->at(0));
483633 }
484634
485635 void
@@ -511,9 +661,7 @@ Sys_sleep(vm_registers_t &r, vm_t &vm) · 25 unmodified lines
511661 const auto period_ms = fp.period;
512662
513663 // [TODO] Thread should yield to another according to the sleep() specification.
514− r.current_thread_state = vm_thread_state_t::release;
515664 std::this_thread::sleep_for(std::chrono::milliseconds(period_ms));
516− r.current_thread_state = vm_thread_state_t::ready;
517665
518666 *fp.ret = 0;
519667 }
@@ -539,11 +687,120 @@ Sys_stream(vm_registers_t &r, vm_t &vm) · 19 unmodified lines
539687 throw vm_system_exception{ "Instruction not implemented" };
540688 }
541689
690+namespace
691+{
692+ bool is_delim(const rune_t c, const vm_string_t &delim)
693+ {
694+ const auto max = delim.get_length();
695+ assert(max > 0);
696+
697+ // Optimization for 3 or less delimiters
698+ switch (max)
699+ {
700+ default:
701+ break;
702+ case 1:
703+ return (delim.get_rune(0) == c);
704+ case 2:
705+ return (delim.get_rune(0) == c) || (delim.get_rune(1) == c);
706+ case 3:
707+ return (delim.get_rune(0) == c) || (delim.get_rune(1) == c) || (delim.get_rune(2) == c);
708+ }
709+
710+ for (auto i = word_t{ 0 }; i < max; ++i)
711+ {
712+ if (delim.get_rune(i) == c)
713+ return true;
714+ }
715+
716+ return false;
717+ }
718+}
719+
542720 void
543721 Sys_tokenize(vm_registers_t &r, vm_t &vm)
544722 {
545723 auto &fp = r.stack.peek_frame()->base<F_Sys_tokenize>();
546− throw vm_system_exception{ "Instruction not implemented" };
724+
725+ // Initialize for failure.
726+ fp.ret->t0 = 0;
727+ dec_ref_count_and_free(vm_alloc_t::from_allocation(fp.ret->t1));
728+ fp.ret->t1 = nullptr;
729+
730+ auto s = vm_alloc_t::from_allocation<vm_string_t>(fp.s);
731+ if (s == nullptr || s->get_length() == 0)
732+ return;
733+
734+ auto curr_node = new vm_list_t{ intrinsic_type_desc::type<pointer_t>() };
735+
736+ // Set the head of the list
737+ fp.ret->t1 = curr_node->get_allocation();
738+
739+ auto delim = vm_alloc_t::from_allocation<vm_string_t>(fp.delim);
740+ if (delim == nullptr || delim->get_length() == 0)
741+ {
742+ // Add supplied string to list
743+ pt_ref(curr_node->value()) = s->get_allocation();
744+ s->add_ref();
745+
746+ fp.ret->t0 = 1;
747+ return;
748+ }
749+
750+ // Tokenize the string
751+ vm_list_t *prev_node = nullptr;
752+ auto n = word_t{ 0 };
753+ auto begin = word_t{ 0 };
754+ auto end = word_t{ 0 };
755+ const auto len = s->get_length();
756+ while (begin < len)
757+ {
758+ // Consume delimiters
759+ while (begin < len)
760+ {
761+ const auto rune = s->get_rune(begin);
762+ if (!is_delim(rune, *delim))
763+ break;
764+ ++begin;
765+ }
766+
767+ end = begin;
768+
769+ // Consume non-delimiters
770+ while (end < len)
771+ {
772+ const auto rune = s->get_rune(end);
773+ if (is_delim(rune, *delim))
774+ break;
775+ ++end;
776+ }
777+
778+ // No progress - It is and isn't a delimiter (i.e. no more characters).
779+ if (begin == end)
780+ break;
781+
782+ // Current node is initially non-null
783+ if (curr_node == nullptr)
784+ curr_node = new vm_list_t{ intrinsic_type_desc::type<pointer_t>() };
785+
786+ const auto token = new vm_string_t{ *s, begin, end };
787+ pt_ref(curr_node->value()) = token->get_allocation();
788+
789+ // Previous node is initially null
790+ if (prev_node != nullptr)
791+ {
792+ prev_node->set_tail(curr_node);
793+ curr_node->release();
794+ }
795+
796+ prev_node = curr_node;
797+ curr_node = nullptr;
798+
799+ begin = end;
800+ ++n;
801+ }
802+
803+ fp.ret->t0 = n;
547804 }
548805
549806 void
@@ -571,7 +828,28 @@ void · 21 unmodified lines
571828 Sys_write(vm_registers_t &r, vm_t &vm)
572829 {
573830 auto &fp = r.stack.peek_frame()->base<F_Sys_write>();
574− throw vm_system_exception{ "Instruction not implemented" };
831+
832+ const auto buffer = vm_alloc_t::from_allocation<vm_array_t>(fp.buf);
833+ assert(buffer->get_element_type()->size_in_bytes == intrinsic_type_desc::type<byte_t>()->size_in_bytes);
834+
835+ auto n = fp.n;
836+ if (buffer == nullptr || n <= 0)
837+ {
838+ *fp.ret = 0;
839+ return;
840+ }
841+
842+ // [SPEC] Supplying a size greater than the buffer length is
843+ // equivalent to indicating the entire buffer should be written.
844+ n = std::min(n, buffer->get_length());
845+
846+ auto fd_alloc = vm_alloc_t::from_allocation(fp.fd);
847+ assert(fd_alloc->alloc_type == T_FD);
848+
849+ auto fd = fd_alloc->get_allocation<vm_fd_t>();
850+ fd->write(n, buffer->at(0));
851+
852+ *fp.ret = n;
575853 }
576854
577855 void