748 // the user hasn't requested/received it yet. |
748 // the user hasn't requested/received it yet. |
749 // If the user wants to port_dissociate before the event has been processed, |
749 // If the user wants to port_dissociate before the event has been processed, |
750 // we should handle things gracefully. To do so, we need to keep an extra |
750 // we should handle things gracefully. To do so, we need to keep an extra |
751 // reference to the cookie around until the event is processed |
751 // reference to the cookie around until the event is processed |
752 // thus the otherwise seemingly extraneous "cookies" map |
752 // thus the otherwise seemingly extraneous "cookies" map |
753 // The key of this map is a pointer to the corresponding &fCookie.cookie |
753 // The key of this map is a pointer to the corresponding fCookie |
754 cookies map[*interface{}]*fileObjCookie |
754 cookies map[*fileObjCookie]struct{} |
755 } |
755 } |
756 |
756 |
757 // PortEvent is an abstraction of the port_event C struct. |
757 // PortEvent is an abstraction of the port_event C struct. |
758 // Compare Source against PORT_SOURCE_FILE or PORT_SOURCE_FD |
758 // Compare Source against PORT_SOURCE_FILE or PORT_SOURCE_FD |
759 // to see if Path or Fd was the event source. The other will be |
759 // to see if Path or Fd was the event source. The other will be |
824 e.mu.Lock() |
825 e.mu.Lock() |
825 defer e.mu.Unlock() |
826 defer e.mu.Unlock() |
826 if _, found := e.paths[path]; found { |
827 if _, found := e.paths[path]; found { |
827 return fmt.Errorf("%v is already associated with this Event Port", path) |
828 return fmt.Errorf("%v is already associated with this Event Port", path) |
828 } |
829 } |
829 fobj, err := createFileObj(path, stat) |
830 fCookie, err := createFileObjCookie(path, stat, cookie) |
830 if err != nil { |
831 if err != nil { |
831 return err |
832 return err |
832 } |
833 } |
833 fCookie := &fileObjCookie{fobj, cookie} |
834 _, err = port_associate(e.port, PORT_SOURCE_FILE, uintptr(unsafe.Pointer(fCookie.fobj)), events, (*byte)(unsafe.Pointer(fCookie))) |
834 _, err = port_associate(e.port, PORT_SOURCE_FILE, uintptr(unsafe.Pointer(fobj)), events, (*byte)(unsafe.Pointer(&fCookie.cookie))) |
|
835 if err != nil { |
835 if err != nil { |
836 return err |
836 return err |
837 } |
837 } |
838 e.paths[path] = fCookie |
838 e.paths[path] = fCookie |
839 e.cookies[&fCookie.cookie] = fCookie |
839 e.cookies[fCookie] = struct{}{} |
840 return nil |
840 return nil |
841 } |
841 } |
842 |
842 |
843 // DissociatePath wraps port_dissociate(3c) for a filesystem path. |
843 // DissociatePath wraps port_dissociate(3c) for a filesystem path. |
844 func (e *EventPort) DissociatePath(path string) error { |
844 func (e *EventPort) DissociatePath(path string) error { |
869 e.mu.Lock() |
869 e.mu.Lock() |
870 defer e.mu.Unlock() |
870 defer e.mu.Unlock() |
871 if _, found := e.fds[fd]; found { |
871 if _, found := e.fds[fd]; found { |
872 return fmt.Errorf("%v is already associated with this Event Port", fd) |
872 return fmt.Errorf("%v is already associated with this Event Port", fd) |
873 } |
873 } |
874 fCookie := &fileObjCookie{nil, cookie} |
874 fCookie, err := createFileObjCookie("", nil, cookie) |
875 _, err := port_associate(e.port, PORT_SOURCE_FD, fd, events, (*byte)(unsafe.Pointer(&fCookie.cookie))) |
|
876 if err != nil { |
875 if err != nil { |
877 return err |
876 return err |
878 } |
877 } |
|
878 _, err = port_associate(e.port, PORT_SOURCE_FD, fd, events, (*byte)(unsafe.Pointer(fCookie))) |
|
879 if err != nil { |
|
880 return err |
|
881 } |
879 e.fds[fd] = fCookie |
882 e.fds[fd] = fCookie |
880 e.cookies[&fCookie.cookie] = fCookie |
883 e.cookies[fCookie] = struct{}{} |
881 return nil |
884 return nil |
882 } |
885 } |
883 |
886 |
884 // DissociateFd wraps calls to port_dissociate(3c) on file descriptors. |
887 // DissociateFd wraps calls to port_dissociate(3c) on file descriptors. |
885 func (e *EventPort) DissociateFd(fd uintptr) error { |
888 func (e *EventPort) DissociateFd(fd uintptr) error { |
894 return err |
897 return err |
895 } |
898 } |
896 if err == nil { |
899 if err == nil { |
897 // dissociate was successful, safe to delete the cookie |
900 // dissociate was successful, safe to delete the cookie |
898 fCookie := e.fds[fd] |
901 fCookie := e.fds[fd] |
899 delete(e.cookies, &fCookie.cookie) |
902 delete(e.cookies, fCookie) |
900 } |
903 } |
901 delete(e.fds, fd) |
904 delete(e.fds, fd) |
902 return err |
905 return err |
903 } |
906 } |
904 |
907 |
905 func createFileObj(name string, stat os.FileInfo) (*fileObj, error) { |
908 func createFileObjCookie(name string, stat os.FileInfo, cookie interface{}) (*fileObjCookie, error) { |
906 fobj := new(fileObj) |
909 fCookie := new(fileObjCookie) |
907 bs, err := ByteSliceFromString(name) |
910 fCookie.cookie = cookie |
908 if err != nil { |
911 if name != "" && stat != nil { |
909 return nil, err |
912 fCookie.fobj = new(fileObj) |
910 } |
913 bs, err := ByteSliceFromString(name) |
911 fobj.Name = (*int8)(unsafe.Pointer(&bs[0])) |
914 if err != nil { |
912 s := stat.Sys().(*syscall.Stat_t) |
915 return nil, err |
913 fobj.Atim.Sec = s.Atim.Sec |
916 } |
914 fobj.Atim.Nsec = s.Atim.Nsec |
917 fCookie.fobj.Name = (*int8)(unsafe.Pointer(&bs[0])) |
915 fobj.Mtim.Sec = s.Mtim.Sec |
918 s := stat.Sys().(*syscall.Stat_t) |
916 fobj.Mtim.Nsec = s.Mtim.Nsec |
919 fCookie.fobj.Atim.Sec = s.Atim.Sec |
917 fobj.Ctim.Sec = s.Ctim.Sec |
920 fCookie.fobj.Atim.Nsec = s.Atim.Nsec |
918 fobj.Ctim.Nsec = s.Ctim.Nsec |
921 fCookie.fobj.Mtim.Sec = s.Mtim.Sec |
919 return fobj, nil |
922 fCookie.fobj.Mtim.Nsec = s.Mtim.Nsec |
|
923 fCookie.fobj.Ctim.Sec = s.Ctim.Sec |
|
924 fCookie.fobj.Ctim.Nsec = s.Ctim.Nsec |
|
925 } |
|
926 return fCookie, nil |
920 } |
927 } |
921 |
928 |
922 // GetOne wraps port_get(3c) and returns a single PortEvent. |
929 // GetOne wraps port_get(3c) and returns a single PortEvent. |
923 func (e *EventPort) GetOne(t *Timespec) (*PortEvent, error) { |
930 func (e *EventPort) GetOne(t *Timespec) (*PortEvent, error) { |
924 pe := new(portEvent) |
931 pe := new(portEvent) |
927 return nil, err |
934 return nil, err |
928 } |
935 } |
929 p := new(PortEvent) |
936 p := new(PortEvent) |
930 e.mu.Lock() |
937 e.mu.Lock() |
931 defer e.mu.Unlock() |
938 defer e.mu.Unlock() |
932 e.peIntToExt(pe, p) |
939 err = e.peIntToExt(pe, p) |
|
940 if err != nil { |
|
941 return nil, err |
|
942 } |
933 return p, nil |
943 return p, nil |
934 } |
944 } |
935 |
945 |
936 // peIntToExt converts a cgo portEvent struct into the friendlier PortEvent |
946 // peIntToExt converts a cgo portEvent struct into the friendlier PortEvent |
937 // NOTE: Always call this function while holding the e.mu mutex |
947 // NOTE: Always call this function while holding the e.mu mutex |
938 func (e *EventPort) peIntToExt(peInt *portEvent, peExt *PortEvent) { |
948 func (e *EventPort) peIntToExt(peInt *portEvent, peExt *PortEvent) error { |
|
949 if e.cookies == nil { |
|
950 return fmt.Errorf("this EventPort is already closed") |
|
951 } |
939 peExt.Events = peInt.Events |
952 peExt.Events = peInt.Events |
940 peExt.Source = peInt.Source |
953 peExt.Source = peInt.Source |
941 cookie := (*interface{})(unsafe.Pointer(peInt.User)) |
954 fCookie := (*fileObjCookie)(unsafe.Pointer(peInt.User)) |
942 peExt.Cookie = *cookie |
955 _, found := e.cookies[fCookie] |
|
956 |
|
957 if !found { |
|
958 panic("unexpected event port address; may be due to kernel bug; see https://go.dev/issue/54254") |
|
959 } |
|
960 peExt.Cookie = fCookie.cookie |
|
961 delete(e.cookies, fCookie) |
|
962 |
943 switch peInt.Source { |
963 switch peInt.Source { |
944 case PORT_SOURCE_FD: |
964 case PORT_SOURCE_FD: |
945 delete(e.cookies, cookie) |
|
946 peExt.Fd = uintptr(peInt.Object) |
965 peExt.Fd = uintptr(peInt.Object) |
947 // Only remove the fds entry if it exists and this cookie matches |
966 // Only remove the fds entry if it exists and this cookie matches |
948 if fobj, ok := e.fds[peExt.Fd]; ok { |
967 if fobj, ok := e.fds[peExt.Fd]; ok { |
949 if &fobj.cookie == cookie { |
968 if fobj == fCookie { |
950 delete(e.fds, peExt.Fd) |
969 delete(e.fds, peExt.Fd) |
951 } |
970 } |
952 } |
971 } |
953 case PORT_SOURCE_FILE: |
972 case PORT_SOURCE_FILE: |
954 if fCookie, ok := e.cookies[cookie]; ok && uintptr(unsafe.Pointer(fCookie.fobj)) == uintptr(peInt.Object) { |
973 peExt.fobj = fCookie.fobj |
955 // Use our stashed reference rather than using unsafe on what we got back |
|
956 // the unsafe version would be (*fileObj)(unsafe.Pointer(uintptr(peInt.Object))) |
|
957 peExt.fobj = fCookie.fobj |
|
958 } else { |
|
959 panic("mismanaged memory") |
|
960 } |
|
961 delete(e.cookies, cookie) |
|
962 peExt.Path = BytePtrToString((*byte)(unsafe.Pointer(peExt.fobj.Name))) |
974 peExt.Path = BytePtrToString((*byte)(unsafe.Pointer(peExt.fobj.Name))) |
963 // Only remove the paths entry if it exists and this cookie matches |
975 // Only remove the paths entry if it exists and this cookie matches |
964 if fobj, ok := e.paths[peExt.Path]; ok { |
976 if fobj, ok := e.paths[peExt.Path]; ok { |
965 if &fobj.cookie == cookie { |
977 if fobj == fCookie { |
966 delete(e.paths, peExt.Path) |
978 delete(e.paths, peExt.Path) |
967 } |
979 } |
968 } |
980 } |
969 } |
981 } |
|
982 return nil |
970 } |
983 } |
971 |
984 |
972 // Pending wraps port_getn(3c) and returns how many events are pending. |
985 // Pending wraps port_getn(3c) and returns how many events are pending. |
973 func (e *EventPort) Pending() (int, error) { |
986 func (e *EventPort) Pending() (int, error) { |
974 var n uint32 = 0 |
987 var n uint32 = 0 |
988 return 0, fmt.Errorf("len(s) (%d) is less than min events requested (%d)", len(s), min) |
1001 return 0, fmt.Errorf("len(s) (%d) is less than min events requested (%d)", len(s), min) |
989 } |
1002 } |
990 got := uint32(min) |
1003 got := uint32(min) |
991 max := uint32(len(s)) |
1004 max := uint32(len(s)) |
992 var err error |
1005 var err error |
993 ps := make([]portEvent, max, max) |
1006 ps := make([]portEvent, max) |
994 _, err = port_getn(e.port, &ps[0], max, &got, timeout) |
1007 _, err = port_getn(e.port, &ps[0], max, &got, timeout) |
995 // got will be trustworthy with ETIME, but not any other error. |
1008 // got will be trustworthy with ETIME, but not any other error. |
996 if err != nil && err != ETIME { |
1009 if err != nil && err != ETIME { |
997 return 0, err |
1010 return 0, err |
998 } |
1011 } |
999 e.mu.Lock() |
1012 e.mu.Lock() |
1000 defer e.mu.Unlock() |
1013 defer e.mu.Unlock() |
|
1014 valid := 0 |
1001 for i := 0; i < int(got); i++ { |
1015 for i := 0; i < int(got); i++ { |
1002 e.peIntToExt(&ps[i], &s[i]) |
1016 err2 := e.peIntToExt(&ps[i], &s[i]) |
1003 } |
1017 if err2 != nil { |
1004 return int(got), err |
1018 if valid == 0 && err == nil { |
1005 } |
1019 // If err2 is the only error and there are no valid events |
|
1020 // to return, return it to the caller. |
|
1021 err = err2 |
|
1022 } |
|
1023 break |
|
1024 } |
|
1025 valid = i + 1 |
|
1026 } |
|
1027 return valid, err |
|
1028 } |