+
+ if (kfse == NULL) { // yikes! no free events
+ int len=0;
+ char *str;
+
+ //
+ // Figure out what kind of reference we have to the
+ // file in this event. This helps us find an event
+ // to combine/collapse into to make room.
+ //
+ // If we have a rename or exchange event then we
+ // don't want to go through the normal path, we
+ // want to "steal" an event instead (which is what
+ // find_an_event() will do if str is null).
+ //
+ arg_type = va_arg(ap, int32_t);
+ if (type == FSE_RENAME || type == FSE_EXCHANGE) {
+ str = NULL;
+ } else if (arg_type == FSE_ARG_STRING) {
+ len = va_arg(ap, int32_t);
+ str = va_arg(ap, char *);
+ } else if (arg_type == FSE_ARG_VNODE) {
+ struct vnode *vp;
+
+ vp = va_arg(ap, struct vnode *);
+ pathbuff = get_pathbuff();
+ pathbuff_len = MAXPATHLEN;
+ if (vn_getpath(vp, pathbuff, &pathbuff_len) != 0 || pathbuff[0] == '\0') {
+ release_pathbuff(pathbuff);
+ pathbuff = NULL;
+ }
+ str = pathbuff;
+ } else {
+ str = NULL;
+ }
+
+ //
+ // This will go through all events and find one that we
+ // can combine with (hopefully), or "collapse" into (i.e
+ // it has the same parent) or in the worst case we have
+ // to "recycle" an event which means that it will combine
+ // two other events and return us the now unused event.
+ // failing all that, find_an_event() could still return
+ // null and if it does then we have a catastrophic dropped
+ // events scenario.
+ //
+ kfse = find_an_event(str, len, NULL, &reuse_type, &longest_match_len);
+
+ if (kfse == NULL) {
+ bail_early:
+
+ unlock_fs_event_list();
+ lock_watch_table();
+
+ for(i=0; i < MAX_WATCHERS; i++) {
+ watcher = watcher_table[i];
+ if (watcher == NULL) {
+ continue;
+ }
+
+ watcher->flags |= WATCHER_DROPPED_EVENTS;
+ fsevents_wakeup(watcher);
+ }
+ unlock_watch_table();
+
+ {
+ struct timeval current_tv;
+
+ num_dropped++;
+
+ // only print a message at most once every 5 seconds
+ microuptime(¤t_tv);
+ if ((current_tv.tv_sec - last_print.tv_sec) > 10) {
+ int ii;
+ void *junkptr=zalloc_noblock(event_zone), *listhead=kfse_list_head.lh_first;
+
+ printf("add_fsevent: event queue is full! dropping events (num dropped events: %d; num events outstanding: %d).\n", num_dropped, num_events_outstanding);
+ printf("add_fsevent: kfse_list head %p ; num_pending_rename %d\n", listhead, num_pending_rename);
+ printf("add_fsevent: zalloc sez: %p\n", junkptr);
+ printf("add_fsevent: event_zone info: %d %p\n", ((int *)event_zone)[0], (void *)((int *)event_zone)[1]);
+ for(ii=0; ii < MAX_WATCHERS; ii++) {
+ if (watcher_table[ii] == NULL) {
+ continue;
+ }
+
+ printf("add_fsevent: watcher %p: num dropped %d rd %4d wr %4d q_size %4d flags 0x%x\n",
+ watcher_table[ii], watcher_table[ii]->num_dropped,
+ watcher_table[ii]->rd, watcher_table[ii]->wr,
+ watcher_table[ii]->eventq_size, watcher_table[ii]->flags);
+ }
+
+ last_print = current_tv;
+ if (junkptr) {
+ zfree(event_zone, junkptr);
+ }
+ }
+ }
+
+ if (pathbuff) {
+ release_pathbuff(pathbuff);
+ pathbuff = NULL;
+ }
+
+ return ENOSPC;
+ }
+
+ if ((type == FSE_RENAME || type == FSE_EXCHANGE) && reuse_type != KFSE_RECYCLED) {
+ panic("add_fsevent: type == %d but reuse type == %d!\n", type, reuse_type);
+ } else if ((kfse->type == FSE_RENAME || kfse->type == FSE_EXCHANGE) && kfse->dest == NULL) {
+ panic("add_fsevent: bogus kfse %p (type %d, but dest is NULL)\n", kfse, kfse->type);
+ } else if (kfse->type == FSE_RENAME || kfse->type == FSE_EXCHANGE) {
+ panic("add_fsevent: we should never re-use rename events (kfse %p reuse type %d)!\n", kfse, reuse_type);
+ }
+
+ if (reuse_type == KFSE_COLLAPSED) {
+ if (str) {
+ const char *tmp_ptr, *new_str;
+
+ //
+ // if we collapsed and have a string we have to chop off the
+ // tail component of the pathname to get the parent.
+ //
+ // NOTE: it is VERY IMPORTANT that we leave the trailing slash
+ // on the pathname. user-level code depends on this.
+ //
+ if (str[0] == '\0' || longest_match_len <= 1) {
+ printf("add_fsevent: strange state (str %s / longest_match_len %d)\n", str, longest_match_len);
+ if (longest_match_len < 0) {
+ panic("add_fsevent: longest_match_len %d\n", longest_match_len);
+ }
+ }
+ // chop off the tail component if it's not the
+ // first character...
+ if (longest_match_len > 1) {
+ str[longest_match_len] = '\0';
+ } else if (longest_match_len == 0) {
+ longest_match_len = 1;
+ }
+
+ new_str = vfs_addname(str, longest_match_len, 0, 0);
+ if (new_str == NULL || new_str[0] == '\0') {
+ panic("add_fsevent: longest match is strange (new_str %p).\n", new_str);
+ }
+
+ lck_rw_lock_exclusive(&event_handling_lock);
+
+ kfse->len = longest_match_len;
+ tmp_ptr = kfse->str;
+ kfse->str = new_str;
+ kfse->ino = 0;
+ kfse->mode = 0;
+ kfse->uid = 0;
+ kfse->gid = 0;
+
+ lck_rw_unlock_exclusive(&event_handling_lock);
+
+ vfs_removename(tmp_ptr);
+ } else {
+ panic("add_fsevent: don't have a vnode or a string pointer (kfse %p)\n", kfse);
+ }
+ }
+
+ if (reuse_type == KFSE_RECYCLED && (type == FSE_RENAME || type == FSE_EXCHANGE)) {
+
+ // if we're recycling this kfse and we have a rename or
+ // exchange event then we need to also get an event for
+ // kfse_dest.
+ //
+ if (did_alloc) {
+ // only happens if we allocated one but then failed
+ // for kfse_dest (and thus free'd the first one we
+ // allocated)
+ kfse_dest = zalloc_noblock(event_zone);
+ if (kfse_dest != NULL) {
+ memset(kfse_dest, 0, sizeof(kfs_event));
+ kfse_dest->refcount = 1;
+ OSBitOrAtomic16(KFSE_BEING_CREATED, &kfse_dest->flags);
+ } else {
+ did_alloc = 0;
+ }
+ }
+
+ if (kfse_dest == NULL) {
+ int dest_reuse_type, dest_match_len;
+
+ kfse_dest = find_an_event(NULL, 0, kfse, &dest_reuse_type, &dest_match_len);
+
+ if (kfse_dest == NULL) {
+ // nothing we can do... gotta bail out
+ goto bail_early;
+ }
+
+ if (dest_reuse_type != KFSE_RECYCLED) {
+ panic("add_fsevent: type == %d but dest_reuse type == %d!\n", type, dest_reuse_type);
+ }
+ }
+ }
+
+
+ //
+ // Here we check for some fast-path cases so that we can
+ // jump over the normal initialization and just get on
+ // with delivering the event. These cases are when we're
+ // combining/collapsing an event and so basically there is
+ // no more work to do (aside from a little book-keeping)
+ //
+ if (str && kfse->len != 0) {
+ kfse->abstime = now;
+ OSAddAtomic(1, (SInt32 *)&kfse->refcount);
+ skip_init = 1;
+
+ if (reuse_type == KFSE_COMBINED) {
+ num_combined_events++;
+ } else if (reuse_type == KFSE_COLLAPSED) {
+ num_added_to_parent++;
+ }
+ } else if (reuse_type != KFSE_RECYCLED) {
+ panic("add_fsevent: I'm so confused! (reuse_type %d str %p kfse->len %d)\n",
+ reuse_type, str, kfse->len);
+ }
+