35
35
#include " egl_utils.h"
36
36
#include " wayland_display.h"
37
37
38
+ #include < sstream>
39
+
38
40
#include < unistd.h>
39
41
#include < sys/syscall.h>
40
42
43
+ #include < sys/eventfd.h>
44
+ #include < fcntl.h>
45
+
41
46
#define DBG_TIMING (x )
42
47
48
+ #define MEMWATCHTAG " memwatcher: "
49
+
43
50
DBG_TIMING (
44
51
#define gettid () syscall(SYS_gettid)
45
52
static uint64_t t00;)
@@ -692,6 +699,8 @@ bool WaylandDisplay::SetupEngine(const std::string &bundle_path, const std::vect
692
699
}
693
700
}
694
701
702
+ SetupMemoryWatcher ();
703
+
695
704
if (window_metrix_skipped_) {
696
705
FlutterWindowMetricsEvent event = {};
697
706
@@ -710,7 +719,143 @@ bool WaylandDisplay::SetupEngine(const std::string &bundle_path, const std::vect
710
719
return true ;
711
720
}
712
721
722
+ void WaylandDisplay::HandleMemoryWatcherEvent () {
723
+ // minimum 20 sec between FlutterEngineNotifyLowMemoryWarning calls
724
+ constexpr auto min_time_between_warnings_ns_ = uint64_t (2e10 );
725
+ const uint64_t now_ns = FlutterEngineGetCurrentTime ();
726
+ constexpr int readsz = 32 ;
727
+
728
+ if (now_ns < memory_watcher_.last_warning_sent_ns + min_time_between_warnings_ns_) {
729
+ return ;
730
+ }
731
+
732
+ lseek (memory_watcher_.memory_file_fd , 0 , SEEK_SET);
733
+
734
+ char memstr[readsz];
735
+ int pos = 0 ;
736
+ int rv = 0 ;
737
+ do {
738
+ rv = read (memory_watcher_.memory_file_fd , memstr + pos, readsz - 1 - pos);
739
+ if (rv != -1 )
740
+ pos += rv;
741
+ } while ((rv == -1 && errno == EINTR) || rv > 0 );
742
+ memstr[pos] = ' \0 ' ;
743
+ if (rv == -1 ) {
744
+ dbgW (MEMWATCHTAG " problem reading memory_usage, errno:%d\n " , errno);
745
+ return ;
746
+ }
747
+
748
+ dbgT (MEMWATCHTAG " current mem: %s\n " , memstr);
749
+ const long mem = atol (memstr);
750
+ if (mem > 0 ) {
751
+ long level = 0 ;
752
+ for (auto memwatchlevel : memory_watcher_.levels ) {
753
+ if (memwatchlevel > mem)
754
+ break ;
755
+ else
756
+ ++level;
757
+ }
758
+ dbgT (MEMWATCHTAG " current memory level: %ld\n " , level);
759
+ if (level > memory_watcher_.current_level ) {
760
+ dbgW (MEMWATCHTAG " sending FlutterEngineNotifyLowMemoryWarning\n " );
761
+ auto ret = FlutterEngineNotifyLowMemoryWarning (engine_);
762
+ if (ret != kSuccess ) {
763
+ dbgE (MEMWATCHTAG " FlutterEngineNotifyLowMemoryWarning failed with %d\n " , int (ret));
764
+ return ;
765
+ }
766
+ memory_watcher_.last_warning_sent_ns = now_ns;
767
+ }
768
+ memory_watcher_.current_level = level;
769
+ }
770
+ }
771
+
772
+ void WaylandDisplay::CleanupMemoryWatcher () {
773
+ if (memory_watcher_.memory_file_fd != -1 ) {
774
+ close (memory_watcher_.memory_file_fd );
775
+ memory_watcher_.memory_file_fd = -1 ;
776
+ }
777
+ if (memory_watcher_.event_fd != -1 ) {
778
+ close (memory_watcher_.event_fd );
779
+ memory_watcher_.event_fd = -1 ;
780
+ }
781
+ }
782
+
783
+ void WaylandDisplay::SetupMemoryWatcher () {
784
+ const std::string cgroup_memory_path = getEnv (" FLUTTER_LAUNCHER_WAYLAND_CGROUP_MEMORY_PATH" , std::string ());
785
+ if (cgroup_memory_path.empty ()) {
786
+ dbgI (MEMWATCHTAG " Memory watcher will not run - no FLUTTER_LAUNCHER_WAYLAND_CGROUP_MEMORY_PATH env var defined\n " );
787
+ } else {
788
+ dbgT (" SetupMemoryWatcher start, cgroup_memory_path: %s\n " , cgroup_memory_path.c_str ());
789
+ const std::string memory_usage_path = cgroup_memory_path + " /memory.usage_in_bytes" ;
790
+ const std::string cgroup_event_control_path = cgroup_memory_path + " /cgroup.event_control" ;
791
+ const std::string memoryWarningLevels = getEnv (" FLUTTER_LAUNCHER_WAYLAND_MEMORY_WARNING_WATERMARK_BYTES" , std::string ());
792
+ if (memoryWarningLevels.empty ()) {
793
+ dbgI (MEMWATCHTAG " Memory watcher will not run - no FLUTTER_LAUNCHER_WAYLAND_MEMORY_WARNING_WATERMARK_BYTES env var defined\n " );
794
+ } else {
795
+ std::stringstream ss{memoryWarningLevels};
796
+ std::string level;
797
+ while (std::getline (ss, level, ' ,' )) {
798
+ if (const long l = atol (level.c_str ())) {
799
+ memory_watcher_.levels .push_back (l);
800
+ } else {
801
+ dbgE (MEMWATCHTAG " Memory watcher will not run - could not perform conversion to long for %s; FLUTTER_LAUNCHER_WAYLAND_MEMORY_WARNING_WATERMARK_BYTES: %s\n " , level.c_str (), memoryWarningLevels.c_str ());
802
+ return ;
803
+ }
804
+ }
805
+ if (memory_watcher_.levels .size () < 1 ) {
806
+ dbgW (MEMWATCHTAG " Memory watcher will not run - needs at least 1 memory level; FLUTTER_LAUNCHER_WAYLAND_MEMORY_WARNING_WATERMARK_BYTES: %s\n " , memoryWarningLevels.c_str ());
807
+ } else {
808
+ int event_control = -1 ;
809
+
810
+ bool all_succeeded = false ;
811
+ auto cleanup = [&](bool *) {
812
+ if (!all_succeeded) {
813
+ CleanupMemoryWatcher ();
814
+ }
815
+ if (event_control != -1 ) {
816
+ close (event_control);
817
+ event_control = -1 ;
818
+ }
819
+ };
820
+ const auto cleanup_watcher = std::unique_ptr<bool , decltype (cleanup)>(&all_succeeded, cleanup);
821
+
822
+ memory_watcher_.memory_file_fd = open (memory_usage_path.c_str (), O_RDONLY);
823
+ if (memory_watcher_.memory_file_fd == -1 ) {
824
+ dbgE (MEMWATCHTAG " Cannot open %s, errno: %d\n " , memory_usage_path.c_str (), errno);
825
+ return ;
826
+ }
827
+
828
+ event_control = open (cgroup_event_control_path.c_str (), O_WRONLY);
829
+ if (event_control == -1 ) {
830
+ dbgE (MEMWATCHTAG " Cannot open %s, errno: %d\n " , cgroup_event_control_path.c_str (), errno);
831
+ return ;
832
+ }
833
+
834
+ memory_watcher_.event_fd = eventfd (0 , 0 );
835
+ if (memory_watcher_.event_fd == -1 ) {
836
+ dbgE (MEMWATCHTAG " eventfd() failed\n " );
837
+ return ;
838
+ }
839
+
840
+ for (auto level : memory_watcher_.levels ) {
841
+ char line[LINE_MAX];
842
+ snprintf (line, LINE_MAX, " %d %d %ld" , memory_watcher_.event_fd , memory_watcher_.memory_file_fd , level);
843
+ dbgT (MEMWATCHTAG " event_control: writing line '%s'\n " , line);
844
+ const int ret = write (event_control, line, strlen (line) + 1 );
845
+ if (ret == -1 ) {
846
+ dbgE (MEMWATCHTAG " Cannot write to cgroup.event_control, errno: %d\n " , errno);
847
+ return ;
848
+ }
849
+ }
850
+ dbgI (MEMWATCHTAG " starting memory watcher\n " );
851
+ all_succeeded = true ;
852
+ }
853
+ }
854
+ }
855
+ }
856
+
713
857
WaylandDisplay::~WaylandDisplay () {
858
+ CleanupMemoryWatcher ();
714
859
715
860
if (engine_) {
716
861
auto result = FlutterEngineShutdown (engine_);
@@ -892,10 +1037,11 @@ bool WaylandDisplay::Run() {
892
1037
do {
893
1038
int rv;
894
1039
895
- struct pollfd fds[3 ] = {
1040
+ struct pollfd fds[4 ] = {
896
1041
{.fd = vsync.sv_ [vsync.SOCKET_READER ], .events = POLLIN, .revents = 0 },
897
1042
{.fd = fd, .events = POLLIN | POLLERR, .revents = 0 },
898
1043
{.fd = key.timer_fd_ , .events = POLLIN | POLLERR, .revents = 0 },
1044
+ {.fd = memory_watcher_.event_fd , .events = POLLIN | POLLERR, .revents = 0 },
899
1045
};
900
1046
901
1047
do {
@@ -905,7 +1051,7 @@ bool WaylandDisplay::Run() {
905
1051
};
906
1052
907
1053
rv = ppoll (&fds[0 ], std::size (fds), &ts, nullptr );
908
- } while (rv == -1 && rv == EINTR);
1054
+ } while (rv == -1 && errno == EINTR);
909
1055
910
1056
if (rv == -1 ) {
911
1057
printf (" ERROR: ppoll returned -1 (errno: %d)\n " , errno);
@@ -957,6 +1103,18 @@ bool WaylandDisplay::Run() {
957
1103
wl_display_cancel_read (display_);
958
1104
}
959
1105
1106
+ if (fds[3 ].revents & POLLIN) {
1107
+ uint64_t result;
1108
+ do {
1109
+ rv = read (fds[3 ].fd , &result, sizeof result);
1110
+ } while (rv == -1 && errno == EINTR);
1111
+ if (rv == -1 ) {
1112
+ dbgE (MEMWATCHTAG " problems reading event fd, errno=%d\n " , errno);
1113
+ } else {
1114
+ HandleMemoryWatcherEvent ();
1115
+ }
1116
+ }
1117
+
960
1118
break ;
961
1119
} while (true );
962
1120
0 commit comments